一行代码将文件存储到本地或各种存储平台

1,137 阅读7分钟

一行代码将文件存储到本地或各种存储平台

这里我们介绍的是一个开源项目。

这个是他的官网

简介 (xuyanwu.cn)

下面来看他的一个介绍:

一行代码将文件存储到本地、FTP、SFTP、WebDAV、阿里云 OSS、华为云 OBS、七牛云 Kodo、腾讯云 COS、百度云 BOS、又拍云 USS、MinIO、 Amazon S3、GoogleCloud Storage、FastDFS、 Azure Blob Storage、Cloudflare R2、金山云 KS3、美团云 MSS、京东云 OSS、天翼云 OOS、移动 云EOS、沃云 OSS、 网易数帆 NOS、Ucloud US3、青云 QingStor、平安云 OBS、首云 OSS、IBM COS、其它兼容 S3 协议的存储平台

下面就开始了解一下这个项目,以及快速入门

快速入门

上传文件

首先我们需要引入pom文件,需要注意的是他是依赖于springboot的环境的

i如果你不是springboot的环境的话

脱离 SpringBoot 单独使用 (xuyanwu.cn)

可以参考这个文章,我这里就不做讲解了。

ps:这里考虑到springboot最大单文件上传是1MB所以需要我们首先配置一下

spring:
  servlet:
    multipart:
      max-file-size: 10MB # 文件大小限制
      max-request-size: 100MB # 请求大小限制

首先引入pom文件

<dependency>
    <groupId>org.dromara.x-file-storage</groupId>
    <artifactId>x-file-storage-spring</artifactId>
    <version>2.1.0</version>
</dependency>

我们首先快速入门就直接用本地来举例。

之后我们来写配置文件

首先是一个基础的配置:

dromara:
  x-file-storage: #文件存储配置
    default-platform: local-plus-1 #默认使用的存储平台 这里和下面的platform是一样的
    thumbnail-suffix: ".min.jpg" #缩略图后缀,例如【.min.jpg】【.png】
    local-plus:
    - platform: local-plus-1 # 存储平台标识
      enable-storage: true  #启用存储
      enable-access: true #启用访问(线上请使用 Nginx 配置,效率更高)
      domain: http://127.0.0.1:8080/file/ # 访问域名,例如:“http://127.0.0.1:8030/file/”,注意后面要和 path-patterns 保持一致,“/”结尾,本地存储建议使用相对路径,方便后期更换域名
      base-path: local-plus/ # 基础路径
      path-patterns: /file/** # 访问路径
      storage-path: D:/Temp/ # 存储路径

这个local-plus就是本地的,如果你用别的平台可以去配置别的平台的。

可以参考官网的这里、

快速入门 (xuyanwu.cn)

image-20240503211345923

之后我们在启动类上加上@EnableFileStorage注解

之后就可以开始使用了

要想使用就要去注入对应的service

@Autowired
private FileStorageService fileStorageService;//注入实列

首先看一个最简单的上传文件

    /**
     * 上传文件
     */
    @PostMapping("/upload")
    public FileInfo upload(MultipartFile file) {
        return fileStorageService.of(file).upload();
    }

之后我们来做一个测试

image-20240503204932864

同时我们也可以看到在本地是有了:

image-20240503204954062

并且打开这个url也是没问题的。

如果你不想要这么多的信息。

也可以只返回一个url

只需要在那个之后加一个getUrl()就可以获得。

fileStorageService.of返回的是一个FileInfo对象,这个对象就是我们刚才测试看到的内容。我们可以通过链式编程获得里面的东西。

同时也可以在上传前设置很多的参数

我们可以通过这个源码来看到

image-20240503205602941

有兴趣的也可以去官网去了解。

这里用的比较多的可能是,setPlatform

这个是用来设置一个上传平台的。可以用作一个备用上传的接口

我们之后来看上传图片

上传图片

/**
 * 上传图片,成功返回文件信息
 * 图片处理使用的是 https://github.com/coobird/thumbnailator
 */
@PostMapping("/upload-image")
public FileInfo uploadImage(MultipartFile file) {
    return fileStorageService.of(file)
            .image(img -> img.size(1000,1000))  //将图片大小调整到 1000*1000
            .thumbnail(th -> th.size(200,200))  //再生成一张 200*200 的缩略图
            .upload();
}

image-20240503210836629

他会返回图片地址以及一个锁视图。

进阶操作

下面讲解一下进阶操作

直接上传 HttpServletRequest

这种方式通过直接读取输入流进行上传,可以实现文件不落盘,边读取边上传,速度更快

需要先在配置文件中开启 multipart 懒加载,不然在 Controller 中拿到输入流是已经被读取过的

spring:
  servlet:
    multipart:
      max-file-size: 10MB # 文件大小限制
      max-request-size: 100MB # 请求大小限制
      resolve-lazily: true
    /**
     * 直接读取 HttpServletRequest 中的文件进行上传,成功返回文件信息
     */
    @PostMapping("/upload-request")
    public FileInfo uploadRequest(HttpServletRequest request) {
        return fileStorageService.of(request).upload();
    }

保存上传记录

可以实现 FileRecorder 这个接口,把文件信息保存到数据库中。

首先我们要创建一个对应的表。

这个是官方推荐的一个sql表:

-- 这里使用的是 mysql
CREATE TABLE `file_detail`
(
    `id`                varchar(32)  NOT NULL COMMENT '文件id',
    `url`               varchar(512) NOT NULL COMMENT '文件访问地址',
    `size`              bigint(20)   DEFAULT NULL COMMENT '文件大小,单位字节',
    `filename`          varchar(256) DEFAULT NULL COMMENT '文件名称',
    `original_filename` varchar(256) DEFAULT NULL COMMENT '原始文件名',
    `base_path`         varchar(256) DEFAULT NULL COMMENT '基础存储路径',
    `path`              varchar(256) DEFAULT NULL COMMENT '存储路径',
    `ext`               varchar(32)  DEFAULT NULL COMMENT '文件扩展名',
    `content_type`      varchar(128) DEFAULT NULL COMMENT 'MIME类型',
    `platform`          varchar(32)  DEFAULT NULL COMMENT '存储平台',
    `th_url`            varchar(512) DEFAULT NULL COMMENT '缩略图访问路径',
    `th_filename`       varchar(256) DEFAULT NULL COMMENT '缩略图名称',
    `th_size`           bigint(20)   DEFAULT NULL COMMENT '缩略图大小,单位字节',
    `th_content_type`   varchar(128) DEFAULT NULL COMMENT '缩略图MIME类型',
    `object_id`         varchar(32)  DEFAULT NULL COMMENT '文件所属对象id',
    `object_type`       varchar(32)  DEFAULT NULL COMMENT '文件所属对象类型,例如用户头像,评价图片',
    `metadata`          text COMMENT '文件元数据',
    `user_metadata`     text COMMENT '文件用户元数据',
    `th_metadata`       text COMMENT '缩略图元数据',
    `th_user_metadata`  text COMMENT '缩略图用户元数据',
    `attr`              text COMMENT '附加属性',
    `file_acl`          varchar(32)  DEFAULT NULL COMMENT '文件ACL',
    `th_file_acl`       varchar(32)  DEFAULT NULL COMMENT '缩略图文件ACL',
    `hash_info`         text COMMENT '哈希信息',
    `upload_id`         varchar(128) DEFAULT NULL COMMENT '上传ID,仅在手动分片上传时使用',
    `upload_status`     int(11)      DEFAULT NULL COMMENT '上传状态,仅在手动分片上传时使用,1:初始化完成,2:上传完成',
    `create_time`       datetime     DEFAULT NULL COMMENT '创建时间',
    PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB DEFAULT CHARSET = utf8 ROW_FORMAT = DYNAMIC COMMENT ='文件记录表';
​
CREATE TABLE `file_part_detail`
(
    `id`          varchar(32) NOT NULL COMMENT '分片id',
    `platform`    varchar(32)  DEFAULT NULL COMMENT '存储平台',
    `upload_id`   varchar(128) DEFAULT NULL COMMENT '上传ID,仅在手动分片上传时使用',
    `e_tag`       varchar(255) DEFAULT NULL COMMENT '分片 ETag',
    `part_number` int(11)      DEFAULT NULL COMMENT '分片号。每一个上传的分片都有一个分片号,一般情况下取值范围是1~10000',
    `part_size`   bigint(20)   DEFAULT NULL COMMENT '文件大小,单位字节',
    `hash_info`   text CHARACTER SET utf8 COMMENT '哈希信息',
    `create_time` datetime     DEFAULT NULL COMMENT '创建时间',
    PRIMARY KEY (`id`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8 COMMENT ='文件分片信息表,仅在手动分片上传时使用';

之后我们创建实体类和mapper

这个我就不写了。最后我会提供源码,源码里面是有的。

之后来看save保存

/**
 * 保存文件信息到数据库
 */
@SneakyThrows
@Override
public boolean save(FileInfo info) {
    FileDetail detail = toFileDetail(info);
    boolean b = save(detail);
    if (b) {
        info.setId(detail.getId());
    }
    return b;
}

tofiledetail是将 FileInfo 转为 FileDetail

以及

/**
 * 根据 url 查询文件信息
 */
@SneakyThrows
@Override
public FileInfo getByUrl(String url) {
    return toFileInfo(getOne(new QueryWrapper<FileDetail>().eq(FileDetail.COL_URL, url)));
}
​
/**
 * 根据 url 删除文件信息
 */
@Override
public boolean delete(String url) {
    remove(new QueryWrapper<FileDetail>().eq(FileDetail.COL_URL, url));
    return true;
}

下载

这个直接看官方写的这几个方法就可以

// 获取文件信息
FileInfo fileInfo = fileStorageService.getFileInfoByUrl("https://file.abc.com/test/a.jpg");
​
// 下载为字节数组
byte[] bytes = fileStorageService.download(fileInfo).bytes();
​
// 下载到文件
fileStorageService.download(fileInfo).file("C:\a.jpg");
​
// 下载到 OutputStream 中
ByteArrayOutputStream out = new ByteArrayOutputStream();
fileStorageService.download(fileInfo).outputStream(out);
​
// 获取 InputStream 手动处理
fileStorageService.download(fileInfo).inputStream(in -> {
    //TODO 读取 InputStream
});
​
// 直接通过文件信息中的 url 下载,省去手动查询文件信息记录的过程
fileStorageService.download("https://file.abc.com/test/a.jpg").file("C:\a.jpg");
​
// 下载缩略图
fileStorageService.downloadTh(fileInfo).file("C:\th.jpg");

删除

//获取文件信息
FileInfo fileInfo = fileStorageService.getFileInfoByUrl("https://file.abc.com/test/a.jpg");
​
//直接删除
fileStorageService.delete(fileInfo);
​
//条件删除
fileStorageService.delete(fileInfo,info -> {
    //TODO 检查是否满足删除条件
    return true;
});
​
//直接通过文件信息中的 url 删除,省去手动查询文件信息记录的过程
fileStorageService.delete("https://file.abc.com/test/a.jpg");

总结

这些就基本上是一个简单的入门了。

如果有更多的操作,可以移入官网了解。

这里的源码在这里:

xiaou61/xiaou-easy-code: 前后端通用解决方案 springboot vue react 原生js (github.com)

在1文件夹里面。