支持所有云的文件上传
一、背景与问题
文件上传是一个很基本的需求,但是每个公司使用的云计算可能不一样,比如有些公司用的是阿里云、华为云、腾讯云、联通、电信等等;但是无论是哪一种云,其实他们都是遵循的 亚马逊的 S3 文件存储协议。
- 支持阿里云、华为云、腾讯云等各类云
- 同时支持 minio,因为minio也是支持 S3协议的
- 如果是私有化部署,还得支持 本地存储
二、架构与思想
- 使用S3协议作为基础进行设计,目的为了满足所有云。
- 记录所有的上传文件,存到数据库
- 保留文件的基础信息: 大小,文件名,时间等等
- 区分 公共文件和私有文件
三、具体使用
3.1、修改配置
SmartAdmin支持的文件上传模式有local、cloud两种,文件上传接口参考:FileController
。
local
:为本地文件上传,文件存储在服务器本地cloud
: 为云文件存储,目前支持主流的云存储厂商阿里云、华为云、七牛云、minio等支持S3协议
1)修改为本地 local存储
第一步将 file.storage.mode
改为 local
# 文件上传 配置
file:
storage:
mode: local
第二步,配置具体的 local
参数: upload-path
和 url-prefix
upload-path
为上传文件的存放路径,可以自行根据需要修改 ;url-prefix
为访问这些文件的url前缀:url-prefix
默认可以为空,为空情况下会使用springboot
的addResourceHandler
进行静态资源映射,默认映射到/upload
的url路径,比如http://192.168.3.188:1024/upload/public/common/a1.png
,具体配置请看类FileConfig
, 配置如下
# 默认为空
file:
storage:
mode: local
local:
upload-path: ${localPath:/home}/smart_admin_v2/upload/ #上传路径
url-prefix: #url前缀,可以为空,系统会默认为 http://[ip][port]/upload/[fileKey]
url-prefix
可以为nginx映射路径等,比如在nginx配置了映射,映射到upload-path
配置的路径
file:
storage:
mode: local
local:
upload-path: ${localPath:/home}/smart_admin_v2/upload/ #上传路径
url-prefix: https://smartadmin.vip/upload #使用nginx映射的路径
2)修改为本地 云或者minio存储
# 文件上传 配置
file:
storage:
mode: cloud # cloud 为云存储;local 为本地存储,则下面的cloud配置将失效
cloud:
region: oss-cn-qingdao # 自行修改
endpoint: oss-cn-qingdao.aliyuncs.com # 自行修改
bucket-name: bucket-1024lab # 自行修改
access-key: # 自行修改
secret-key: # 自行修改
url-prefix: https://${file.storage.cloud.bucket-name}.${file.storage.cloud.endpoint}/
private-url-expire-seconds: 3600 # url 失效时间
3.2、定义文件存放位置
如果所有文件都存放到一起,那么后续想做个分类都好方便,所以这里需要定义一下 文件目录 FileFolderTypeEnum.java
,建议按照功能业务大块拆分:
public enum FileFolderTypeEnum implements BaseEnum {
COMMON(1, FileFolderTypeEnum.FOLDER_PUBLIC + "/common/", "通用"),
NOTICE(2, FileFolderTypeEnum.FOLDER_PUBLIC + "/notice/", "公告"),
HELP_DOC(3, FileFolderTypeEnum.FOLDER_PUBLIC + "help-doc", "帮助中心"),
FEEDBACK(4, FileFolderTypeEnum.FOLDER_PUBLIC + "/feedback/", "意见反馈"),
;
3.3、上传
对应前端组件
在前端提供了file-preview
、file-preview-modal
、file-upload
三个文件相关的组件可供使用;
序列化与反序列化@JsonDeserialize(using = FileKeyVoDeserializer.class)
此反序列化,用于将前端传输的fileVO
的JSON数组转化为可供数据库直接存储的fileKey
字符串。@JsonSerialize(using = FileKeyVoDeserializer.class)
此序列化,用于将fileKey
字符串转化为可供前端直接使用的fileVO
的JSON数组。
在业务上逗号分割
在数据存储上一般在对应的商品表中创建cover_pic
字段,此字段用于存储文件key信息,多张图片的话,一般采用逗号分割的方式存储此字段。 字段定义方式:
@ApiModelProperty("商品封面")
@Length(max = 250, message = "商品封面最多250字符")
@JsonSerialize(using = FileKeyVoSerializer.class)
@JsonDeserialize(using = FileKeyVoDeserializer.class)
private String coverPic;
SmartAdmin的前端文件上传组件,返回的JSON数据是以fileVO
JSON数组的方式返回的,为了减少前后端数据二次处理的繁琐工作,特意增加了JSON的序列化和反序列化处理。
除了上面的两个序列化类外,还提供了FileKeySerializer.class
,此类可将fileKey
字符串转化为文件请求全路径地址。
比如 反序列化:
@Data
public class NoticeUpdateFormVO extends NoticeVO {
@ApiModelProperty("附件")
@JsonSerialize(using = FileKeyVoSerializer.class)
private String attachment;
@ApiModelProperty("可见范围")
private List<NoticeVisibleRangeVO> visibleRangeList;
}
比如 序列化:
@Data
public class NoticeDetailVO {
@ApiModelProperty("id")
private Long noticeId;
@ApiModelProperty("标题")
private String title;
@ApiModelProperty("附件")
@JsonSerialize(using = FileKeyVoSerializer.class)
private String attachment;
四、实现原理
4.1、表结构
表结构用于记录:文件基本信息:大小、文件名、时间、上传人信息,表设计如下
4.2、亚马逊S3协议
亚马逊S3协议有java库:
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.11.842</version>
</dependency>
4.3、文件实现类
为了满足 本地、云存储 多种方式,系统中定义了 接口IFileStorageService
文件接口;
并提供两种实现类:
FileStorageCloudServiceImpl.java 使用亚马逊S3协议的实现类
FileStorageLocalServiceImpl.java 本地存储实现类
具体如何判断使用本地存储实现类还是使用 云S3存储实现类,请看 sa-base
项目中的 FileCloudConfig
,使用了条件注解@ConditionalOnProperty
,代码如下:
@Bean
@ConditionalOnProperty(prefix = "file.storage", name = {"mode"}, havingValue = "cloud")
public IFileStorageService initCloudFileService() {
return new FileStorageCloudServiceImpl();
}
@Bean
@ConditionalOnProperty(prefix = "file.storage", name = {"mode"}, havingValue = "local")
public IFileStorageService initLocalFileService() {
return new FileStorageLocalServiceImpl();
}
4.4、 缓存
知道,对于某些私有化的文件,当访问的时候需要后端
请求云计算
生成一个可以访问的 url地址
,并且这个url地址有个过期时间
;
但是文件服务又是一个很基础的服务,获取访问地址,后端需要发请求,是阻塞的
,如果频繁的调用,会很慢,所以做了一个redis缓存;
private String getCacheUrl(String fileKey) {
String redisKey = redisService.generateRedisKey(RedisKeyConst.Support.FILE_URL, fileKey);
String fileUrl = redisService.get(redisKey);
if (null != fileUrl) {
return fileUrl;
}
ResponseDTO<String> responseDTO = fileStorageService.getFileUrl(fileKey);
if (!responseDTO.getOk()) {
return null;
}
fileUrl = responseDTO.getData();
redisService.set(redisKey, fileUrl, fileStorageService.cacheExpireSecond());
return fileUrl;
}
4.5、 文件key生成规则
32位 uuid + 文件格式后缀
比如:c0e6e9340a8c4c4aa8c8062bdc5f8bcc.png
联系我们
- 教育领域(高职院校数字化、就业创业大数据平台、继续教育平台;在线教育系统、视频直播、题库等,包含:医学、应急管理、成考、专升本等)
- 供应链领域(网络货运平台、大宗贸易进销存ERP、物流管理TMS、B2B电商、仓储WMS、AI提效等)
- 中医领域(诊所数字化管理、互联网医院、AI辅助诊疗、中医适宜技术、在线云问诊、空中药房等)
- AI+软件领域(软件定制外包、开源技术、数据大屏、国产化改造、技术升级换代、人员外包、技术顾问、技术培训等)
加微信: 卓大 拉你入群,一起学习 | 公众号 :六边形工程师 分享:赚钱、代码、生活 | 请 “1024创新实验室” 烩面里加肉 咖啡配胡辣汤,提神又饱腹 | 抖音 : 六边形工程师 直播:赚钱、代码、中医 |