3.7 腾讯云COS图片上传接口,application-local文件保存隐私数据

This commit is contained in:
zhangsan 2025-03-07 15:47:21 +08:00
parent b809fb9a6b
commit f222b215d8
8 changed files with 334 additions and 2 deletions

4
.gitignore vendored
View File

@ -67,6 +67,10 @@ rebel.xml
# Docker 相关
docker-compose.override.yml
# 排除本地开发环境配置(如 Spring Boot 的 application-local.yml
application-local.yml
application-local.*
# 如果你用到 IDEA 自带的 File-Based Storage8+版本默认),可以添加:
# .idea/.name
# .idea/gradle.xml

View File

@ -51,6 +51,12 @@
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser-4.9</artifactId>
</dependency>
<!-- 对象存储 -->
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>cos_api</artifactId>
<version>5.6.227</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>

View File

@ -21,3 +21,29 @@ create table if not exists user
UNIQUE KEY uk_userAccount (user_account),
INDEX idx_userName (user_name)
) comment '用户' collate = utf8mb4_unicode_ci;
-- 图片表
create table if not exists picture
(
id bigint auto_increment comment 'id' primary key,
url varchar(512) not null comment '图片 url',
name varchar(128) not null comment '图片名称',
introduction varchar(512) null comment '简介',
category varchar(64) null comment '分类',
tags varchar(512) null comment '标签JSON 数组)',
pic_size bigint null comment '图片体积',
pic_width int null comment '图片宽度',
pic_height int null comment '图片高度',
pic_scale double null comment '图片宽高比例',
pic_format varchar(32) null comment '图片格式',
user_id bigint not null comment '创建用户 id',
create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间',
edit_time datetime default CURRENT_TIMESTAMP not null comment '编辑时间',
update_time datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
is_delete tinyint default 0 not null comment '是否删除',
INDEX idx_name (name), -- 提升基于图片名称的查询性能
INDEX idx_introduction (introduction), -- 用于模糊搜索图片简介
INDEX idx_category (category), -- 提升基于分类的查询性能
INDEX idx_tags (tags), -- 提升基于标签的查询性能
INDEX idx_userId (user_id) -- 提升基于用户 ID 的查询性能
) comment '图片' collate = utf8mb4_unicode_ci;

View File

@ -15,7 +15,6 @@ import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
@Aspect

View File

@ -0,0 +1,58 @@
package edu.whut.smilepicturebackend.config;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.region.Region;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "cos.client")
@Data
public class CosClientConfig {
/**
* 域名
*/
private String host;
/**
* secretId
*/
private String secretId;
/**
* 密钥注意不要泄露
*/
private String secretKey;
/**
* 区域
*/
private String region;
/**
* 桶名
*/
private String bucket;
@Bean
public COSClient cosClient() {
// 1 初始化用户身份信息secretId, secretKey
// SECRETID SECRETKEY 请登录访问管理控制台 https://console.cloud.tencent.com/cam/capi 进行查看和管理
COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
// 2 设置 bucket 的地域, COS 地域的简称请参见 https://cloud.tencent.com/document/product/436/6224
// clientConfig 中包含了设置 region, https(默认 http), 超时, 代理等 set 方法, 使用可参见源码或者常见问题 Java SDK 部分
ClientConfig clientConfig = new ClientConfig(new Region(region));
// 这里建议设置使用 https 协议
// 5.6.54 版本开始默认使用了 https
clientConfig.setHttpProtocol(HttpProtocol.https);
// 3 生成 cos 客户端
return new COSClient(cred, clientConfig);
}
}

View File

@ -0,0 +1,114 @@
package edu.whut.smilepicturebackend.controller;
import com.qcloud.cos.model.COSObject;
import com.qcloud.cos.model.COSObjectInputStream;
import com.qcloud.cos.utils.IOUtils;
import edu.whut.smilepicturebackend.annotation.AuthCheck;
import edu.whut.smilepicturebackend.common.BaseResponse;
import edu.whut.smilepicturebackend.common.ResultUtils;
import edu.whut.smilepicturebackend.constant.UserConstant;
import edu.whut.smilepicturebackend.exception.BusinessException;
import edu.whut.smilepicturebackend.exception.ErrorCode;
import edu.whut.smilepicturebackend.manager.CosManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
@Slf4j
@RestController
@RequestMapping("/file")
public class FileController {
@Resource
private CosManager cosManager;
/**
* 测试文件上传
*
* @param multipartFile
* @return
*/
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
@PostMapping("/test/upload")
public BaseResponse<String> testUploadFile(@RequestPart("file") MultipartFile multipartFile) {
// 文件目录
String filename = multipartFile.getOriginalFilename();
String filepath = String.format("/test/%s", filename);
try (InputStream in = multipartFile.getInputStream()) {
cosManager.putObject(
filepath,
in,
multipartFile.getSize(),
multipartFile.getContentType()
);
return ResultUtils.success(filepath);
} catch (Exception e) {
log.error("file upload error, filepath = {}", filepath, e);
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "上传失败");
}
// File file = null;
// try {
// // 上传文件
// file = File.createTempFile(filepath, null);
// multipartFile.transferTo(file); //临时文件适配这个接口
// cosManager.putObject(filepath, file);
// // 返回可访问的地址
// return ResultUtils.success(filepath);
// } catch (Exception e) {
// log.error("file upload error, filepath = " + filepath, e);
// throw new BusinessException(ErrorCode.SYSTEM_ERROR, "上传失败");
// } finally {
// if (file != null) {
// // 删除临时文件
// boolean delete = file.delete();
// if (!delete) {
// log.error("file delete error, filepath = {}", filepath);
// }
// }
// }
}
/**
* 测试文件下载
*
* @param filepath 文件路径
* @param response 响应对象
*/
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
@GetMapping("/test/download/")
public void testDownloadFile(String filepath, HttpServletResponse response) throws IOException {
COSObjectInputStream cosObjectInput = null;
try {
COSObject cosObject = cosManager.getObject(filepath);
cosObjectInput = cosObject.getObjectContent();
byte[] bytes = IOUtils.toByteArray(cosObjectInput);
// 设置响应头
response.setContentType("application/octet-stream;charset=UTF-8");
response.setHeader("Content-Disposition", "attachment; filename=" + filepath);
// 写入响应
response.getOutputStream().write(bytes);
response.getOutputStream().flush();
} catch (Exception e) {
log.error("file download error, filepath = " + filepath, e);
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "下载失败");
} finally {
// 释放流
if (cosObjectInput != null) {
cosObjectInput.close();
}
}
}
}

View File

@ -0,0 +1,110 @@
package edu.whut.smilepicturebackend.manager;
import cn.hutool.core.io.FileUtil;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.model.*;
import com.qcloud.cos.model.ciModel.persistence.PicOperations;
import edu.whut.smilepicturebackend.config.CosClientConfig;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
@Component
public class CosManager {
@Resource
private CosClientConfig cosClientConfig;
@Resource
private COSClient cosClient;
/**
* 上传对象
*
* @param key 唯一键
* @param file 文件
*/
public PutObjectResult putObject(String key, File file) {
PutObjectRequest putObjectRequest = new PutObjectRequest(cosClientConfig.getBucket(), key,
file);
return cosClient.putObject(putObjectRequest);
}
/**
* 上传对象接收InputStream文件流
* @param key
* @param in
* @param contentLength
* @param contentType
*/
public void putObject(String key, InputStream in, long contentLength, String contentType) {
ObjectMetadata meta = new ObjectMetadata();
meta.setContentLength(contentLength);
meta.setContentType(contentType);
// 其它 metadata 设置...
cosClient.putObject(cosClientConfig.getBucket(), key, in, meta);
}
/**
* 下载对象
*
* @param key 唯一键
*/
public COSObject getObject(String key) {
GetObjectRequest getObjectRequest = new GetObjectRequest(cosClientConfig.getBucket(), key);
return cosClient.getObject(getObjectRequest);
}
/**
* 上传对象附带图片信息
*
* @param key 唯一键
* @param file 文件
*/
public PutObjectResult putPictureObject(String key, File file) {
PutObjectRequest putObjectRequest = new PutObjectRequest(cosClientConfig.getBucket(), key,
file);
// 对图片进行处理获取基本信息也被视作为一种图片的处理
PicOperations picOperations = new PicOperations();
// 1 表示返回原图信息
picOperations.setIsPicInfo(1);
// 图片处理规则列表
List<PicOperations.Rule> rules = new ArrayList<>();
// 1. 图片压缩转成 webp 格式
String webpKey = FileUtil.mainName(key) + ".webp";
PicOperations.Rule compressRule = new PicOperations.Rule();
compressRule.setFileId(webpKey);
compressRule.setBucket(cosClientConfig.getBucket());
compressRule.setRule("imageMogr2/format/webp");
rules.add(compressRule);
// 2. 缩略图处理仅对 > 20 KB 的图片生成缩略图
if (file.length() > 2 * 1024) {
PicOperations.Rule thumbnailRule = new PicOperations.Rule();
// 拼接缩略图的路径
String thumbnailKey = FileUtil.mainName(key) + "_thumbnail." + FileUtil.getSuffix(key);
thumbnailRule.setFileId(thumbnailKey);
thumbnailRule.setBucket(cosClientConfig.getBucket());
// 缩放规则 /thumbnail/<Width>x<Height>>如果大于原图宽高则不处理
thumbnailRule.setRule(String.format("imageMogr2/thumbnail/%sx%s>", 256, 256));
rules.add(thumbnailRule);
}
// 构造处理参数
picOperations.setRules(rules);
putObjectRequest.setPicOperations(picOperations);
return cosClient.putObject(putObjectRequest);
}
/**
* 删除对象
*
* @param key 唯一键
*/
public void deleteObject(String key) {
cosClient.deleteObject(cosClientConfig.getBucket(), key);
}
}

View File

@ -4,6 +4,8 @@ server:
context-path: /api
spring:
profiles:
active: local
application:
name: smile-picture-backend
# 数据库配置
@ -12,6 +14,10 @@ spring:
url: jdbc:mysql://localhost:3306/smile-picture
username: root
password: 123456
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
mybatis-plus:
type-aliases-package: edu.whut.smilepicturebackend.model.entity
@ -37,4 +43,13 @@ knife4j:
default:
api-rule: package
api-rule-resources:
- edu.whut.smilepicturebackend.controller #接口地址
- edu.whut.smilepicturebackend.controller #接口地址
# 对象存储配置(需要从腾讯云获取)
cos:
client:
host: ${smile-picture.cos.client.host}
secretId: ${smile-picture.cos.client.secretId}
secretKey: ${smile-picture.cos.client.secretKey}
region: ${smile-picture.cos.client.region}
bucket: ${smile-picture.cos.client.bucket}