1.1 简介
开源项目spring-file-storage,在 SpringBoot 中通过简单的方式将文件存储到 本地、FTP、SFTP、WebDAV、谷歌云存储、阿里云OSS、华为云OBS、七牛云Kodo、腾讯云COS、百度云 BOS、又拍云USS、MinIO、 AWS S3、金山云 KS3、美团云 MSS、京东云 OSS、天翼云 OOS、移动云 EOS、沃云 OSS、 网易数帆 NOS、Ucloud US3、青云 QingStor、平安云 OBS、首云 OSS、IBM COS、其它兼容 S3 协议的平台。
1.2 架构图
1.3 实现逻辑
1、核心实现逻辑是定义文件存储接口FileStorage,在FileStorage内抽象若干功能方法,目前支持的功能包括获取云服务商平台、设置平台、保存文件、上传文件、删除文件、下载文件、下载缩略图文件、 释放相关资源等,后各存储服务提供商根据其SDK实现对应方法。
@Getter
@Setter
public class AliyunOssFileStorage implements FileStorage {
/* 存储平台 */
private String platform;
private String accessKey;
private String secretKey;
private String endPoint;
private String bucketName;
private String domain;
private String basePath;
private OSS client;
// 封装实现ossSDK
public boolean save(FileInfo fileInfo, UploadPretreatment pre) {
}
}
2、除此之外,还定义了多个切面调用链,支持在文件上传、下载、删除等过程中输出相关日志信息,只需要实现FileStorageAspect接口就可以自定义切面,并支持动态增减切面。
//获得切面 List
CopyOnWriteArrayList<FileStorageAspect> list = fileStorageService.getAspectList();
//增加
FileStorageAspect aspect = new LogFileStorageAspect();
list.add(aspect);
//删除
list.remove(aspect);
//条件删除
list.removeIf(item -> item instanceof LogFileStorageAspect);
3、支持文件元数据管理:除了文件内容本身,还可以存储一些与文件相关的元数据信息,例如文件名称、大小、上传时间等。设计时可以考虑使用数据库或索引服务来存储和管理这些元数据,以方便文件的检索和管理。
@Service
public class FileDetailService extends ServiceImpl<FileDetailMapper,FileDetail> implements FileRecorder {
/**
* 保存文件信息到数据库
*/
@SneakyThrows
@Override
public boolean record(FileInfo info) {
FileDetail detail = BeanUtil.copyProperties(info,FileDetail.class,"attr");
//这是手动获 取附加属性字典 并转成 json 字符串,方便存储在数据库中
if (info.getAttr() != null) {
detail.setAttr(new ObjectMapper().writeValueAsString(info.getAttr()));
}
boolean b = save(detail);
if (b) {
info.setId(detail.getId());
}
return b;
}
}
1.4 项目分析
优点:
- 扩展性强:可支持动态扩展市面绝大部分云服务存储提供商,切换成本小
- 使用简单:集成方便,只需要引入云存储依赖、spring-file-storage依赖、配置相关信息即可使用
缺点:
3. 文件上传、下载都是在后端实现,接口效率受服务器内存、带宽限制,对大文件上传/下载不友好
4. 不支持临时文件链接的获取,前端无法根据生成的已授权的文件地址直接put上传文件
测试数据:
文件上传 | MinIO | OSS |
---|---|---|
30MB | 1.2秒 | 131秒 |
206MB | 6秒 | 230秒 |
2.34GB | 67秒 | - |
文件下载 | MinIO | OSS |
---|---|---|
30MB | 1秒 | 35秒 |
206MB | 2秒 | 330秒 |
2.34GB | 26秒 | - |
1.5 项目集成
1. 引入pom依赖
<dependency>
<groupId>com.nimbus</groupId>
<artifactId>spring-file-storage</artifactId> <!--已封装好主要云存储服务商的依赖-->
<version>0.7.0</version>
</dependency>
- yaml自定义存储服务提供商
spring:
file-storage: #文件存储配置
default-platform: aliyun-oss-1 # 默认使用的存储平台
thumbnail-suffix: ".min.jpg" # 缩略图后缀,例如【.min.jpg】【.png】
local-plus: # 本地存储
- platform: local-plus-1 # 存储平台标识
enable-storage: true # 启用存储
enable-access: true # 启用访问(线上请使用 Nginx 配置,效率更高)
domain: "D:/Temp/" # 访问域名,例如:“http://127.0.0.1:8030/”,注意后面要和 path-patterns 保持一致,“/”结尾,本地存储建议使用相对路径,方便后期更换域名
base-path: local-plus/ # 基础路径
path-patterns: /** # 访问路径
storage-path: D:/Temp/ # 存储路径 D:\Temp
aliyun-oss: # 阿里云 OSS
- platform: aliyun-oss-1 # 存储平台标识
enable-storage: true # 启用存储
access-key: ${aliyun.oss.access_key}
secret-key: ${aliyun.oss.access_secret}
end-point: ${aliyun.oss.endpoint}
bucket-name: ${aliyun.oss.bucket_name}
domain: ${aliyun.oss.host} # 访问域名,注意“/”结尾
base-path: infra-common/ # 基础路径
minio: # MinIO
- platform: minio-1
enable-storage: true
access-key: ??
secret-key: ??
end-point: ??
bucket-name: ??
domain: ??
base-path: hy/
3. 表设计
-- mysql
DROP TABLE IF EXISTS `file_detail`;
CREATE TABLE `file_detail`
(
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '文件id',
`url` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '文件访问地址',
`size` bigint(20) NULL DEFAULT NULL COMMENT '文件大小,单位字节',
`filename` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件名称',
`original_filename` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '原始文件名',
`base_path` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '基础存储路径',
`path` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '存储路径',
`ext` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件扩展名',
`content_type` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'MIME类型',
`platform` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '存储平台',
`th_url` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '缩略图访问路径',
`th_filename` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '缩略图名称',
`th_size` bigint(20) NULL DEFAULT NULL COMMENT '缩略图大小,单位字节',
`th_content_type` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '缩略图MIME类型',
`object_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件所属对象id',
`object_type` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件所属对象类型,例如用户头像,评价图片',
`attr` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '附加属性',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB
AUTO_INCREMENT = 1
CHARACTER SET = utf8
COLLATE = utf8_general_ci COMMENT = '文件记录表'
ROW_FORMAT = Dynamic;
-- postgre 自定义
create table if not exists "file_detail" (
id VARCHAR(255) not null,
url VARCHAR(255) not null,
"size" BIGINT null,
filename VARCHAR(255) null,
original_filename VARCHAR(255) null,
base_path VARCHAR(255) null,
"path" VARCHAR(255) null,
ext VARCHAR(255) null,
content_type VARCHAR(255) null,
platform VARCHAR(255) null,
th_url VARCHAR(255) null,
th_filename VARCHAR(255) null,
th_size bigint null,
th_content_type VARCHAR(255) null,
object_id VARCHAR(255) null,
object_type VARCHAR(255) null,
attr VARCHAR(255) null,
create_time TIMESTAMP null,
project_id UUID null,
tenant_id UUID null,
primary key ("id")
);
-- Column comments
COMMENT ON TABLE file_detail IS '文件记录表';
COMMENT ON COLUMN public.file_detail.id IS '主键';
COMMENT ON COLUMN public.file_detail.url IS '文件访问地址';
COMMENT ON COLUMN public.file_detail."size" IS '文件大小,单位字节';
COMMENT ON COLUMN public.file_detail.filename IS '文件名称';
COMMENT ON COLUMN public.file_detail.original_filename IS '原始文件名';
COMMENT ON COLUMN public.file_detail.base_path IS '基础存储路径';
COMMENT ON COLUMN public.file_detail."path" IS '存储路径';
COMMENT ON COLUMN public.file_detail.ext IS '文件扩展名';
COMMENT ON COLUMN public.file_detail.content_type IS 'MIME类型';
COMMENT ON COLUMN public.file_detail.platform IS '存储平台';
COMMENT ON COLUMN public.file_detail.th_url IS '缩略图访问路径';
COMMENT ON COLUMN public.file_detail.th_filename IS '缩略图名称';
COMMENT ON COLUMN public.file_detail.th_size IS '缩略图大小,单位字节';
COMMENT ON COLUMN public.file_detail.th_content_type IS '缩略图MIME类型';
COMMENT ON COLUMN public.file_detail.object_id IS '文件所属对象id';
COMMENT ON COLUMN public.file_detail.object_type IS '文件所属对象类型,例如用户头像,评价图';
COMMENT ON COLUMN public.file_detail.attr IS '附加属性';
COMMENT ON COLUMN public.file_detail.create_time IS '创建时间';
COMMENT ON COLUMN public.file_detail.project_id IS '项目id';
COMMENT ON COLUMN public.file_detail.tenant_id IS '租户id';
``