1024创新实验室-公告

助力抖音1000个粉丝,开播写代码🎉🎉

打开【抖音APP】-点击【左上角侧边栏】-【点击扫一扫】-【进行关注】🎉🎉

和1024创新实验室一起,热爱代码,热爱生活,永远年轻,永远前行🎉🎉


Skip to content

支持所有云的文件上传

一、背景与问题

文件上传是一个很基本的需求,但是每个公司使用的云计算可能不一样,比如有些公司用的是阿里云、华为云、腾讯云、联通、电信等等;但是无论是哪一种云,其实他们都是遵循的 亚马逊的 S3 文件存储协议。

  • 支持阿里云、华为云、腾讯云等各类云
  • 同时支持 minio,因为minio也是支持 S3协议的
  • 如果是私有化部署,还得支持 本地存储

二、架构与思想

  • 使用S3协议作为基础进行设计,目的为了满足所有云。
  • 记录所有的上传文件,存到数据库
  • 保留文件的基础信息: 大小,文件名,时间等等
  • 区分 公共文件和私有文件

三、具体使用

3.1、修改配置

SmartAdmin支持的文件上传模式有local、cloud两种,文件上传接口参考:FileController

  • local:为本地文件上传,文件存储在服务器本地
  • cloud: 为云文件存储,目前支持主流的云存储厂商阿里云、华为云、七牛云、minio等支持S3协议

1)修改为本地 local存储

第一步将 file.storage.mode 改为 local

yaml
# 文件上传 配置
file:
  storage:
    mode: local

第二步,配置具体的 local 参数: upload-pathurl-prefix

  • upload-path 为上传文件的存放路径,可以自行根据需要修改 ; url-prefix为访问这些文件的url前缀:
  • url-prefix默认可以为空,为空情况下会使用springbootaddResourceHandler进行静态资源映射,默认映射到/upload的url路径,比如http://192.168.3.188:1024/upload/public/common/a1.png ,具体配置请看类FileConfig, 配置如下
yaml
# 默认为空
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配置的路径
yaml
file:
  storage:
    mode: local
    local:
      upload-path: ${localPath:/home}/smart_admin_v2/upload/  #上传路径
      url-prefix: https://smartadmin.vip/upload               #使用nginx映射的路径

2)修改为本地 云或者minio存储

yaml
# 文件上传 配置
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,建议按照功能业务大块拆分:

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-previewfile-preview-modalfile-upload 三个文件相关的组件可供使用;

序列化与反序列化
@JsonDeserialize(using = FileKeyVoDeserializer.class) 此反序列化,用于将前端传输的fileVO的JSON数组转化为可供数据库直接存储的fileKey字符串。
@JsonSerialize(using = FileKeyVoDeserializer.class) 此序列化,用于将fileKey字符串转化为可供前端直接使用的fileVO的JSON数组。
在业务上逗号分割
在数据存储上一般在对应的商品表中创建cover_pic字段,此字段用于存储文件key信息,多张图片的话,一般采用逗号分割的方式存储此字段。 字段定义方式:

java
    @ApiModelProperty("商品封面")
    @Length(max = 250, message = "商品封面最多250字符")
    @JsonSerialize(using = FileKeyVoSerializer.class)
    @JsonDeserialize(using = FileKeyVoDeserializer.class)
    private String coverPic;

SmartAdmin的前端文件上传组件,返回的JSON数据是以fileVOJSON数组的方式返回的,为了减少前后端数据二次处理的繁琐工作,特意增加了JSON的序列化和反序列化处理。
除了上面的两个序列化类外,还提供了FileKeySerializer.class,此类可将fileKey字符串转化为文件请求全路径地址。

比如 反序列化:

java
@Data
public class NoticeUpdateFormVO extends NoticeVO {

    @ApiModelProperty("附件")
    @JsonSerialize(using = FileKeyVoSerializer.class)
    private String attachment;

    @ApiModelProperty("可见范围")
    private List<NoticeVisibleRangeVO> visibleRangeList;
}

比如 序列化:

java
@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库:

xml
<dependency>
  <groupId>com.amazonaws</groupId>
  <artifactId>aws-java-sdk-s3</artifactId>
  <version>1.11.842</version>
</dependency>

4.3、文件实现类

为了满足 本地、云存储 多种方式,系统中定义了 接口IFileStorageService 文件接口;
并提供两种实现类:

java
FileStorageCloudServiceImpl.java   使用亚马逊S3协议的实现类
FileStorageLocalServiceImpl.java   本地存储实现类

具体如何判断使用本地存储实现类还是使用 云S3存储实现类,请看 sa-base项目中的 FileCloudConfig,使用了条件注解@ConditionalOnProperty,代码如下:

java
   @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缓存;

java
    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


联系我们

1024创新实验室-主任:卓大,混迹于各个技术圈,研究过计算机,熟悉点 java,略懂点前端。
1024创新实验室 致力于成为中原领先、国内一流的技术团队, 以AI+数字化为驱动,用技术为产业互联网提供无限可能, 业务如下:
  • 教育领域(高职院校数字化、就业创业大数据平台、继续教育平台;在线教育系统、视频直播、题库等,包含:医学、应急管理、成考、专升本等)
  • 供应链领域(网络货运平台、大宗贸易进销存ERP、物流管理TMS、B2B电商、仓储WMS、AI提效等)
  • 中医领域(诊所数字化管理、互联网医院、AI辅助诊疗、中医适宜技术、在线云问诊、空中药房等)
  • AI+软件领域(软件定制外包、开源技术、数据大屏、国产化改造、技术升级换代、人员外包、技术顾问、技术培训等)
加微信: 卓大
拉你入群,一起学习
公众号 :六边形工程师
分享:赚钱、代码、生活
请 “1024创新实验室”
烩面里加肉
咖啡配胡辣汤,提神又饱腹
抖音 : 六边形工程师
直播:赚钱、代码、中医