3.7 腾讯云COS图片上传接口,application-local文件保存隐私数据
This commit is contained in:
parent
b809fb9a6b
commit
f222b215d8
4
.gitignore
vendored
4
.gitignore
vendored
@ -67,6 +67,10 @@ rebel.xml
|
||||
# Docker 相关
|
||||
docker-compose.override.yml
|
||||
|
||||
# 排除本地开发环境配置(如 Spring Boot 的 application-local.yml)
|
||||
application-local.yml
|
||||
application-local.*
|
||||
|
||||
# 如果你用到 IDEA 自带的 File-Based Storage(8+版本默认),可以添加:
|
||||
# .idea/.name
|
||||
# .idea/gradle.xml
|
||||
|
6
pom.xml
6
pom.xml
@ -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>
|
||||
|
@ -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;
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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}
|
Loading…
x
Reference in New Issue
Block a user