image.png上传下载

项目支持将文件上传到三类存储器:

  1. 兼容 S3 协议的对象存储:支持 MinIO、腾讯云 COS、七牛云 Kodo、华为云 OBS、亚马逊 S3 等等。
  2. 磁盘存储:本地、FTP 服务器、SFTP 服务器。
  3. 数据库存储:MySQL、Oracle、PostgreSQL、SQL Server 等等。

技术选型?

  • 优先,✔ 推荐方案 1。如果无法使用云服务,可以自己搭建一个 MinIO 服务。
  • 其次,推荐方案 3。数据库的主从机制可以实现高可用,备份也方便,少量小文件问题不大。
  • 最后,× 不推荐方案 2。主要是实现高可用比较困难,无法实现故障转移。

    1. 快速入门

    本小节,我们来添加个文件配置,并使用它上传下载文件。

    1.1 新增配置

    ① 打开 [基础设施 -> 文件管理 -> 文件配置] 菜单,进入文件配置的界面。
    image.png
    ② 点击 [新增] 按钮,选择存储器为【S3 对象存储器】,并填写七牛云的配置。如下图:
    image.png

  • 节点地址:s3-cn-south-1.qiniucs.com

  • 存储 bucket:ruoyi-vue-pro
  • accessKey:b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8
  • accessSecret:kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP
  • 自定义域名:http://test.yudao.iocoder.cn

③ 添加完后,点击该配置所在行的 [测试] 按钮,测试配置是否正确。
image.png
④ 测试通过后,点击该配置所在行的 [主配置] 按钮,设置它为默认的配置,后续使用它进行文件的上传。

1.2 上传文件

① 点击 [基础设施 -> 文件管理 -> 文件列表] 菜单,进入文件列表的界面。
image.png
② 点击 [上传文件] 按钮,选择要上传的文件。
image.png
③ 上传完成后,如果想要删除,可点击该文件所在行的 [删除] 按钮。

2. 文件上传

项目提供了 2 种文件上传的方式,分别适合前端、后端使用。

2.1 方式一:前端上传

module/infra/controller/admin/file/FileController.java提供了 /admin-api/infra/file/upload RESTful API,用于前端直接上传文件。

  1. // FileController.java
  2. @PostMapping("/upload")
  3. @ApiOperation("上传文件")
  4. @ApiImplicitParams({
  5. @ApiImplicitParam(name = "file", value = "文件附件", required = true, dataTypeClass = MultipartFile.class),
  6. @ApiImplicitParam(name = "path", value = "文件路径", example = "yudaoyuanma.png", dataTypeClass = String.class)
  7. })
  8. public CommonResult<String> uploadFile(@RequestParam("file") MultipartFile file,
  9. @RequestParam("path") String path) throws IOException {
  10. return success(fileService.createFile(path, IoUtil.readBytes(file.getInputStream())));
  11. }

前端上传文件的代码如何实现,可见:

  • 文件列表,文件上传 src/views/infra/file/index.vue
  • 个人中心,头像修改 views/system/user/profile/userAvatar.vue

    2.2 方式二:后端上传

    ykkj-module-infra 的 module/infra/api/file/FileApi.java提供了 #createFile(…) 方法,用于后端需要上传文件的逻辑。 ```java // FileApi.java /**
    • 保存文件,并返回文件的访问路径 *
    • @param path 文件路径
    • @param content 文件内容
    • @return 文件路径 */ String createFile(String path, byte[] content);
  1. 例如说,个人中心修改头像时,需要进行头像的上传。如下图所示:<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/28522586/1652860590480-42ab3a1d-a89d-45ed-a6ea-4fe500ae31ce.png#clientId=u3eb3b1da-e94e-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=252&id=u19a39fc8&margin=%5Bobject%20Object%5D&name=image.png&originHeight=252&originWidth=1112&originalType=binary&ratio=1&rotation=0&showTitle=false&size=55333&status=done&style=none&taskId=u3715cdce-0353-4baf-9757-230b35f53d3&title=&width=1112)<br />注意,需要使用到后端上传的 Maven 模块,需要引入 ykkj-module-infra-api 依赖。例如说 ykkj-module-system-biz 模块的 pom.xml 文件,引用如下:
  2. ```xml
  3. <dependency>
  4. <groupId>com.ykkj.boot</groupId>
  5. <artifactId>ykkj-module-infra-api</artifactId>
  6. <version>${revision}</version>
  7. </dependency>

3. 文件下载

文件上传成功后,返回的是完整的 URL 访问路径
不同的文件存储器,返回的 URL 路径的规则是不同的:
① 当存储器是【S3 对象存储】时,支持 HTTP 访问,所以直接使用 S3 对象存储返回的 URL 路径即可。
② 当存储器是【数据库】【本地磁盘】等时,它们只支持存储,所以需要 module/infra/controller/admin/file/FileController.java提供的 /admin-api/infra/file/{configId}/get/{path} RESTful API,读取文件内容后返回。

// FileController.java

@GetMapping("/{configId}/get/{path}")
@ApiOperation("下载文件")
@ApiImplicitParams({
    @ApiImplicitParam(name = "configId", value = "配置编号",  required = true, dataTypeClass = Long.class),
    @ApiImplicitParam(name = "path", value = "文件路径", required = true, dataTypeClass = String.class)
})
public void getFileContent(HttpServletResponse response,
                           @PathVariable("configId") Long configId,
                           @PathVariable("path") String path) throws IOException {
    byte[] content = fileService.getFileContent(configId, path);
    if (content == null) {
        log.warn("[getFileContent][configId({}) path({}) 文件不存在]", configId, path);
        response.setStatus(HttpStatus.NOT_FOUND.value());
        return;
    }
    ServletUtils.writeAttachment(response, path, content);
}

4. 文件客户端

技术组件 ykkj-spring-boot-starter-file,定义了 framework/file/core/client/FileClient.java接口,抽象了文件客户端的方法。

public interface FileClient {

    /**
     * 获得客户端编号
     *
     * @return 客户端编号
     */
    Long getId();

    /**
     * 上传文件
     *
     * @param content 文件流
     * @param path 相对路径
     * @return 完整路径,即 HTTP 访问地址
     */
    String upload(byte[] content, String path);

    /**
     * 删除文件
     *
     * @param path 相对路径
     */
    void delete(String path);

    /**
     * 获得文件的内容
     *
     * @param path 相对路径
     * @return 文件的内容
     */
    byte[] getContent(String path);

}

FileClient 有 5 个实现类,使用不同存储器进行文件的上传与下载。UML 类图如所示:
image.png
文件上传的调用的 UML 时序图如下所示:
image.png

5. S3 对象存储的配置

做的不错的云存储服务,都是兼容 S3 协议的。如何获取对应的 S3 配置,整理到了 framework/file/core/client/s3/S3FileClientConfig.java配置类。
有一点要注意,云存储服务的 Bucket 需要设置为公共读,不然 URL 无法访问到文件。
并且,最好使用自定义域名,方便迁移到不同的云存储服务。