目录
- 一、需求分析
- 二、库表设计
- 三、图片的处理
- 如何实现图片的上传和下载
- 创建图片的业务流程
- 如何对图片进行解析
- 四、创建并使用对象存储
- 五、后端操作对象存储
- 初始化客户端
- 通用能力类
- 文档上传
- 文件下载
一、需求分析
管理员功能:
- 图片的上传和创建:仅管理员使用,支持选择本地图片的上传,填写相关信息,比如名称、简介、标签、分类等。系统会自动解析图片的基础信息(如宽高和格式等),便于检索。
- 图片管理:管理员可以对图库内的图片资源进行管理,包括查询和删除。
- 图片修改(可以编辑信息):管理员可以对图片信息进行编辑,例如可以修改图片名称、标签、简介、分类等。
用户功能:
- 用户可以查看与搜索图片列表(主页):用户可以在主页上按关键词搜索图片,并支持按分类、标签等筛选条件分页查看图片列表。
- 查看图片详情:用户点击列表中的图片后,可进入详情页,查看图片的大图及相关信息,如名称、简介、分类、标签、其它图片信息(如宽高和格式等)
- 图片下载:用户在详情页可以点击下载图片按钮,将图片保存到本地。
二、库表设计
表名为picture
,SQL设计如下:
-- 图片表
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 数组)',
picSize bigint null comment '图片体积',
picWidth int null comment '图片宽度',
picHeight int null comment '图片高度',
picScale double null comment '图片宽高比例',
picFormat varchar(32) null comment '图片格式',
userId bigint not null comment '创建用户 id',
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',
editTime datetime default CURRENT_TIMESTAMP not null comment '编辑时间',
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
isDelete 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 (userId) -- 提升基于用户 ID 的查询性能
) comment '图片' collate = utf8mb4_unicode_ci;
三、图片的处理
如何实现图片的上传和下载
使用对象存储来进行图片的存储:
- 这里使用的对象存储服务是腾讯云的COS,可以通过控制台、API、SDK等方式来快速接入COS,同时可以进行多格式文件的上传和下载。
创建图片的业务流程
创建图片主要是包括两个过程:第一个过程是上传图片文件本身,第二个过程是将图片信息上传到数据库。
有两种常见的处理方式:
- 先上传再提交数据(大多数的处理方式):用户直接上传图片,系统自动生成图片的url存储地址;然后在用户填写其它相关信息并提交后才将图片记录保存到数据库中。
- 上传图片时直接记录图片信息:云图库平台中图片作为核心资源,只要用户将图片上传成功南无就应该把这个图片上传到数据库中(即用户上传图片后系统应该立即生成图片的完整数据记录和其它元信息,这里元信息指的是图片的一些基础信息,这些信息应该是在图片上传成功后就能够解析出来),无需等待用户上传提交图片信息就会立即存入数据库中,这样会使整个交互过程更加轻量。这样的话用户只需要再上传图片的其它信息即可,这样就相当于用户对已有的图片信息进行编辑。
当然我们也可以对用户进行一些限制,比如说当用户上传过多的图片资源时就禁止该用户继续上传图片资源。
如何对图片进行解析
根据具体的需求我们还需要获取到图片的信息,比如图片的宽度、高度、宽高比、文件格式、图片大小、图片名称。
常见的图片解析方式有两种:
- 在后端服务器直接处理图片的方式:比如java库ImagelO、Python中的Pillow,或者说适用范围更加广泛的OpenCV。
- 通过第三方云存储服务的方式:比如腾讯云COS、AWS S3或者图片处理API(比如ImageMagick、ExifTool)直接提取图片的元数据。
本项目中使用腾讯云COS对象存储来实现图片资源的上传和下载,腾讯云COS对象存储支持在图片上传时通过数据万象服务直接获取到图片的各种基础信息:
四、创建并使用对象存储
前往腾讯云搜索对象存储:
点击存储桶列表:
创建存储桶:
地域一般选择要服务的用户的地域,选择公有读私有写,内容安全不要开(要需要交米);然后点击下一步即可
下面三个都不要开,服务端加密选择不加密:
然后点击下一步创建就好了。创建成功后控制台就会出现下图的内容:
创建成功后可以在控制台中上传文件:
一定要注意:对象存储不要给别人看到。接下来通过后端来操作对象存储。
五、后端操作对象存储
初始化客户端
我们需要引入依赖来操作对象存储。可以参考官方文档
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>cos_api</artifactId>
<version>5.6.227</version>
</dependency>
另外腾讯云提供了云API
来帮助我们进行接口的测试,比如说获取文件、查看文件的信息等等.
现在我们参照官方文档来初始化客户端。
-
要注意防止密码泄露,所以新建
application-local.yml文件
,并且在.gitignore文件
中忽略该文件的提交,这样就不会将代码等敏感配置提交到代码仓库中去了。 -
填写配置文件:新建
application-local.yml文件
,并在.gitignore
中忽略该文件的提交,这样就不会将代码等敏感配置提交到代码仓库。application-local.yml文件
配置如下:
# 对象存储配置(需要从腾讯云获取)
cos:
client:
host: xxx
secretId: xxx
secretKey: xxx
region: xxx
bucket: xxx
-
选择更改配置:
-
在项目的
config
包下新建CosClientConfig
类。负责读取配置文件,代码如下:
@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() {
// 初始化用户身份信息(secretId, secretKey)
COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
// 设置bucket的区域, COS地域的简称请参照 https://www.qcloud.com/document/product/436/6224
ClientConfig clientConfig = new ClientConfig(new Region(region));
// 生成cos客户端
return new COSClient(cred, clientConfig);
}
}
通用能力类
新建manager包(一般指可以复用的代码,我们也可以将manager包复制到别的项目中):
- 在该包下新建
CosManager类
提供通用的对象存储操作(比如文件上传和下载),该类需要引入对象存储配置和COS客户端,用于和COS进行交互,代码如下:
@Component
public class CosManager {
@Resource
private CosClientConfig cosClientConfig;
@Resource
private COSClient cosClient;
// ... 一些操作 COS 的方法
}
接下来就是编写文档上传和下载的方法。
文档上传
这里可以参考官方文档部分,在CosManagert
中新增上传对象的方法,代码如下:
/**
* 上传对象
*
* @param key 唯一键
* @param file 文件
*/
public PutObjectResult putObject(String key, File file) {
PutObjectRequest putObjectRequest = new PutObjectRequest(cosClientConfig.getBucket(), key,
file);
return cosClient.putObject(putObjectRequest);
}
接下来我们在controller包中新建FileController
来编写测试文件上传接口。核心流程是先接受用户上传的文件,指定上传的路径,然后调用cosManager.putObject方法
上传文件到 COS 对象存储;上传成功后,会返回一个文件的 key(其实就是文件路径),便于我们访问和下载文件。注意:测试接口一定要加上管理员权限,防止任何用户随意上传文件。
测试文件上传接口代码如下:
/**
* 测试文件上传
*
* @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);
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);
}
}
}
}
下面进行接口测试,使用
local配置启动项目
,如下图:
除了这种方法外,也可以在主配置文件中指定激活的环境配置。
spring:
profiles:
active: local
然后使用Swagger接口文档进行测试(上传之前不要忘记登录)
文件下载
有好几种文件下载方式,由于项目中的图片是公开的,所以我们直接通过URL路径链接访问,适用于单一的,可以被用户公开访问的资源。
(1)在CosManager类中新增对象下载方法,根据对象的key获取存储信息:
/**
* 下载对象
*
* @param key 唯一键
*/
public COSObject getObject(String key) {
GetObjectRequest getObjectRequest = new GetObjectRequest(cosClientConfig.getBucket(), key);
return cosClient.getObject(getObjectRequest);
}
(2)在FileController
中编写测试文件下载接口。
核心流程是根据路径获取到COS文件对象,然后将文件对象转换为文件流,并写入到Servlet的 Response对象中。注意要设置文件下载专属的响应头。测试接口一定要加上管理员权限!防止任何用户随意上传文件。
测试文件下载接口代码如下:
/**
* 测试文件下载
*
* @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();
}
}
}
(3)启动项目,打开Swagger接口文档,测试文件下载:
到这里,通用的文件上传下载的代码已经完成。在其它项目中,我们完全可以直接使用CosManager
中的代码