文件存储引擎模块封装和使用分享

发布时间 2023-11-09 13:21:55作者: Zonezzc

image

背景

需求

在项目开发过程中,经常会使用到文件存储相关的功能,如:

  1. 存储发票文件
  2. 提供发票下载地址
  3. ……

调研

诸如此类的功能就需要使用到本地存储或云服务商提供的存储功能。当然,这对于开发高手的 zone 来说都是小意思,上网一查,对象存储哪家强?

image

第一位赫然显示了百家号创作者推荐的阿里云 OSS 服务,那行,就你吧,还要赶紧摸鱼等下班呢。接 API 嘛,小意思分分钟搞定,写个工具类封装一点上传下载方法那有多难。

开发

在下班点过了 1 小时之后 zone 终于写完了基于 OSS 对象存储服务封装的 FileUploadUtil.java​,并且与业务方法结合,实现了产品的需求。

同事问:「为什么是 FileUploadUtil​,光上传了那下载呢?」
zone 淡定回答:「奥,下载也在工具类里面,用 OssUtil​ 命名怕后来的同事找不到,FileUploadUtil​ 多见名知意呀。」
同事问:「下载方法也在里面那为什么不用 FileUtil​ 命名工具类呢?」
zone 淡定回答:「当然是因为 FileUtil​ 被之前的开发占用掉了,我这个工具类里面有云存储,当然比 FileUtil​ 更高级咯,得区分开来。」
同事问:「行吧,大佬 NB,回头我直接抄了昂。」
zone 淡定回答:「随便用。」

多项目复用

半年时间匆匆流逝,曾经的 FileUploadUtil​ 工具类随着 zone 和同事娴熟的 CV 大法,在公司的项目里遍地开花。闲暇时间,每当看到新接手的项目中不是由自己 V 过去的 FileUploadUtil​,自豪感爆棚,我太牛了。

服务商迁移

但是好景不长,今天新来的技术总监说 OSS 的云服务太贵了,我们要「降本」,更换 AWS 的 S3 对象存储能免费用一年,所有项目都给我迁。随着总监的一声令下,正摸鱼的 zone 不爽了,由于公司里 80% 的项目用的都是 FileUploadUtil​,迁移的事情不出所料的落在了自己头上。得,曾经觉的自己有多牛,如今就觉的自己有多苦。活还是得干,zone 看了一下 S3 的接口文档,刷刷刷的把 FileUploadUtil​ 给改了,顺便把剩下的 20% 项目里也 V 过去了新改到接入 S3 的 FileUploadUtil​。

技术总监验收之后说:「小伙子做的不错,降本这件事情我肯定记你一笔。」就这样 zone 亲眼看到总监真就打开了云笔记记了一笔,就再也没了下文。期待的升职加薪还没有发生,半个月后新的运维总监来了,直接以数据安全为由说服了爱国老板,说要把对象存储迁回 OSS。身为底层开发人员的 zone 当然什么也不知道,只看到任务系统来了个紧急任务,标题「云存储服务迁移」内容直接就三字「迁回去」。zone 看了看手里的奶茶和任务系统里的驳回选项,忍住了即将破口而出的爆珠还是选择嚼碎了吞下去,毕竟生活已然不易,更容不得一丝一毫的浪费,忍了。

多重实现导致 P0 事件

就是这次,还发现了一些隐藏问题。之前的那 20% 的项目中,为了抓紧时间下班陪妹子,并没有把别的同事写的 ossClient​ 对象删除掉,人是偷了懒,但程序可不惯着,这回 zone 在把 FileUploadUtil​ 换回来的时候,项目直接启动失败。这咋回事啊,查看报错信息,原来是有同名 Bean!

APPLICATION FAILED TO START

Description:

The bean 'ossClient', defined in class path resource [com/zonezzc/FileUploadUtil.class], could not be registered. A bean with that name has already been defined in class path resource [com/zonezzc/OssConfiguration.class] and overriding is disabled.

Action:

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

已与地址为 ''127.0.0.1:52291',传输: '套接字'' 的目标虚拟机断开连接

修复这个问题的同时,zone 仔细搜了搜代码,发现这还不是最坑的,最坑的是竟然还有更多的 ossClient​ Bean 并不叫这个名字,叫什么 ossClient1​、ossClient2​,由于之前没有仔细检查,在切换了 S3 服务半个月后的今天,竟然还有文件的上传和下载在使用 OSS 服务,这把 zone 吓得出了一身冷汗,赶紧修复了代码并上报了技术总监。技术总监也不含糊,10 分钟后就定了个 P0 事件给捅上去了,20 分钟后全公司就通过邮件知道了 zone 这个名字,通宵达旦的问题修复就此展开了序幕。

复盘

经过这次 P0 事件,zone 痛定思痛,决定从根本上解决。

之前存在的技术问题

  1. 工具类的使用没有在公司内进行培训,导致 CV 现象和重新开发并存,没有形成统一。
  2. 没有考虑文件存储服务商变更的可能性,导致服务商变更之后不能实现快速安全切换。
  3. 没有抽象文件存储功能,​FileUploadUtil​ 工具类代码侵入性太高,没有与业务系统解耦。

看着这些问题,zone 把脑袋一拍,接着就有了一个大胆的想法,不如就趁这次,设计一个「文件存储引擎」。

概念

image

设计

模板模式

StorageEngine​效果

第一版

实现内容

.
├── java
│   └── com
│       └── zonezzc
│           └── storage
│               └── engine
│                   ├── StorageApplication.java
│                   ├── config
│                   │   ├── CosStorageEngineConfig.java
│                   │   ├── KodoStorageEngineConfig.java
│                   │   └── OssStorageEngineConfig.java
│                   ├── core
│                   │   ├── AbstractStorageEngine.java
│                   │   ├── StorageEngine.java
│                   │   └── context
│                   │       ├── DeleteFileContext.java
│                   │       ├── DownloadUrlContext.java
│                   │       ├── ReadFileContext.java
│                   │       └── StoreFileContext.java
│                   ├── cos
│                   │   └── CosStorageEngine.java
│                   ├── kodo
│                   │   └── KodoStorageEngine.java
│                   ├── oss
│                   │   └── OssStorageEngine.java
│                   └── utils
│                       └── FileUtils.java
└── resources
    ├── application-dev.yml
    ├── application-prod.yml
    ├── application-test.yml
    └── application.yml

# application.yml
storage:
  engine:
    oss:
      endpoint: oss-cn-shanghai.aliyuncs.com
      accessKeyId: xxxxxxxxxxxxxxxxxxxxxxxx
      accessKeySecret: xxxxxxxxxxxxxxxxxxxxxxxx
      bucketName: zonezzc
    cos:
      secretId: xxxxxxxxxxxxxxxxxxxxxxxx
      secretKey: xxxxxxxxxxxxxxxxxxxxxxxx
      region: ap-shanghai
      bucketName: zonezzc-1253326811
    kodo:
      region: z2
      accessKey: xxxxxxxxxxxxxxxxxxxxxxxx
      secretKey: xxxxxxxxxxxxxxxxxxxxxxxx
      bucketName: zonezzc

使用方式

CV 大法,在项目中把所有代码复制粘贴,人工适配

注入 StorageEngine 对象
    @Resource
    private StorageEngine storageEngine;
使用 StorageEngine 提供的文件上传下载方法

image​第二版

实现内容

image​使用方式

  1. 引入对应的文件存储引擎依赖
  2. 根据提示配置 applecation.yml
  3. 注入 StorageEngine ​对象
  4. 使用 ​StorageEngine​ 提供的文件上传下载方法
引入依赖
<dependency>
    <groupId>com.zonezzc</groupId>
    <artifactId>storage-engine-oss</artifactId>
    <version>0.0.1</version>
</dependency>
配置引擎需要的信息

image

com:
  zonezzc:
    storage:
      engine:
        oss:
          access-key-id: xxxxxxxxxxxxxxxxxxxxxxxx
          access-key-secret: xxxxxxxxxxxxxxxxxxxxxxxx
          endpoint: oss-cn-shanghai.aliyuncs.com
          bucket-name: zonezzc
          auto-create-bucket: true

注入 StorageEngine 对象
    @Resource
    private StorageEngine storageEngine;

使用 StorageEngine 提供的文件上传下载方法

image