编辑 | blame | 历史 | 原始文档

文件上传集成指南

概述

本系统提供了**统一的文件上传服务**,支持多种存储方式(本地、S3/OSS、数据库、FTP/SFTP)。

重要:所有业务模块必须使用通用文件上传接口,**禁止为每个模块新增上传接口**。


一、通用上传接口

1.1 接口信息

方法 路径 说明
POST /infra/file/upload 通用文件上传

1.2 请求参数

参数 类型 必填 说明
file MultipartFile 上传的文件
directory String 存储目录,如 purchase-requestcontract

1.3 响应示例

{
  "code": 0,
  "data": "https://xxx.com/xxx.pdf",
  "msg": "操作成功"
}

返回值 data 即为文件访问 URL,可直接存储到数据库。


二、前端集成

2.1 上传组件示例

<el-upload
  :action="uploadUrl"
  :headers="headers"
  :data="{ directory: 'purchase-request' }"
  :on-success="handleUploadSuccess"
  :before-upload="beforeUpload"
>
  <el-button type="primary">点击上传</el-button>
</el-upload>

2.2 配置说明

data() {
  return {
    // 通用上传接口
    uploadUrl: process.env.VUE_APP_BASE_API + '/infra/file/upload',
    // 认证头
    headers: { Authorization: 'Bearer ' + getToken() },
  }
},
methods: {
  handleUploadSuccess(response) {
    if (response.code === 0) {
      this.form.fileUrl = response.data  // 保存文件 URL
    }
  },
  beforeUpload(file) {
    // 业务层校验文件类型和大小
    const isLt10M = file.size / 1024 / 1024 < 10
    if (!isLt10M) {
      this.$message.error('文件大小不能超过 10MB!')
    }
    return isLt10M
  }
}

三、目录规范

各业务模块通过 directory 参数区分存储目录,**禁止新增业务上传接口**:

模块 directory 说明
用户头像 avatar 用户头像图片
合同附件 contract 合同相关文件
采购申请 purchase-request 采购申请附件
采购订单 purchase-order 采购订单附件
产品图片 product 产品相关图片
通用附件 attachment 通用业务附件

命名规范:使用小写字母和连字符,如 purchase-request,与模块名保持一致。


四、后端集成(特殊场景)

4.1 后端需要主动上传文件时

极少数场景下,后端需要主动上传文件(如生成报表后保存),可注入 FileApi

@Resource
private FileApi fileApi;

// 上传文件
String url = fileApi.createFile(bytes, "报表.xlsx", "report", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");

4.2 获取私有文件预签名地址

// 生成临时访问地址,1小时有效
String presignedUrl = fileApi.presignGetUrl(fileUrl, 3600);

五、存储配置

存储配置通过管理后台动态配置,无需修改代码:

  1. 访问 基础设施 > 文件管理 > 文件配置
  2. 新增配置,选择存储类型并填写参数
  3. 点击"设置为主配置"激活使用

支持的存储类型

类型 说明 适用场景
DB 存储到数据库 小文件、临时文件
LOCAL 本地磁盘存储 内网部署、开发环境
FTP/SFTP FTP 服务器 兼容老系统
S3 阿里云 OSS / 腾讯云 COS / MinIO 生产环境

六、文件存储方式

6.1 多文件存储(推荐)

使用 List<String> + StringListTypeHandler,参考 CRM 跟进记录模块:

实体类:

@TableName(value = "crm_follow_up_record", autoResultMap = true)
public class CrmFollowUpRecordDO extends BaseDO {

    /**
     * 图片
     */
    @TableField(typeHandler = StringListTypeHandler.class)
    private List<String> picUrls;

    /**
     * 附件
     */
    @TableField(typeHandler = StringListTypeHandler.class)
    private List<String> fileUrls;
}

数据库字段VARCHAR,存储 JSON 数组格式如 ["url1","url2","url3"]

VO 类:

@Schema(description = "附件")
private List<String> fileUrls;

前端使用:

// 上传成功后追加
handleUploadSuccess(response) {
  if (response.code === 0) {
    if (!this.form.fileUrls) {
      this.form.fileUrls = []
    }
    this.form.fileUrls.push(response.data)
  }
}

// 删除文件
handleRemove(index) {
  this.form.fileUrls.splice(index, 1)
}

6.2 单文件存储

// 实体类
private String fileUrl;

// VO 类
@Schema(description = "附件地址")
private String fileUrl;

6.3 infra_file 表的作用

系统 infra_file 表记录所有上传文件的元数据(name、path、url、type、size),不与业务关联,仅用于:
- 文件管理后台查看上传记录
- 统计存储使用情况


七、注意事项

  1. 使用通用接口:所有文件上传必须使用 /infra/file/upload,禁止新增业务上传接口
  2. 区分目录:通过 directory 参数区分业务模块,便于管理和清理
  3. 文件大小:默认限制 10MB,可在 Nginx 或应用层调整
  4. 类型校验:前端应校验允许的文件类型,防止上传恶意文件
  5. URL 存储:上传成功后,将返回的 URL 存储到业务表的 fileUrl 字段

七、常见问题

Q: 为什么不能为每个模块新增上传接口?

A: 统一接口便于:
- 统一管理存储配置
- 统一权限控制和审计
- 避免代码重复
- 后续维护和迁移

Q: 如何区分不同模块的文件?

A: 使用 directory 参数,文件会存储在对应目录下。

Q: 前端如何限制文件类型?

A: 在 beforeUpload 方法中校验 file.typefile.name 后缀。