文件上传与下载
文件上传下载统一由文件模块完成,文件模块会有独立的鉴权模式。业务模块只需关联文件表即可,无需存储文件。
文件上传
统一使用 upload
接口内的分片上传模式,前端上传使用通用的组件 SpUpload 完成。
非分片上传
有些场景可能无法使用分片上传,可以使用独立上传接口。
请求方式:POST https://${fileApiUrl}/upload/file
请求参数
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
file | File | 是 | 文件 |
publicResourceType | PublicResourceType | 否 | 公共资源文件类型,仅接收特定枚举值,在 PublicResourceType 中查看 |
返回参数
参数名 | 类型 | 说明 |
---|---|---|
fileId | Long | 文件主键 id |
fileType | String | 文件类型 |
contentType | String | 文件内容类型 如:application/x-gzip |
fileSize | Long | 文件大小 单位:bytes |
downloadUrl | String | 文件下载地址 |
thumbnailUrl | String | 缩略图地址,如果是图片需要有缩略图地址 |
文件下载
提示
需要在 nginx 中添加文件访问代理配,详见 配置开发环境代理
所有文件都不能直接下载,需要进行签名认证,这需要在查询文件列表时使用 SpFileHelper 进行签名后再返回给前端。 或当用户点击下载后再进行签名,根据自己的实际业务选择对应的方式。两种方式都要注意用户的鉴权。
图片预览
- 缩略图也需要进行签名,与下载文件一致
- 富文本中的图片也需要进行签名,后台返回富文本信息时要做统一处理
数据导出
当导出较大文件时,推荐使用先写入服务器磁盘再下载文件的模式进行,可以直接使用 getTempFileStorageRootPath
获取存储根目录,使用 uuid 生成文件名。
- 使用 SpFileHelper 获取文件存储目录进行文件写入。
- 使用 SpFileHelper 讲写好的文件下载目录签名后提供给前端进行下载。
重要
- 临时文件存储目录
temp
下的文件,在文件模块启动时会自动删除,所以请勿在临时文件目录下存储重要文件。 - 务必直接使用 uuid 创建文件名,防止文件名重复导致文件被覆盖。
- 请勿在这么目录下创建文件夹,应该直接写入文件,这样清理程序才能正确的清理文件。
导出 excel 文件样例代码
// 获取存储目录
String filePath = spFileHelper.getTempFileStorageRootPath();
String fileName = UUIDUtils.uuidVoidSplit();
// 省略文件写入过程...
// 文件写入成功获取下载地址
String downloadUrl = spFileHelper.getTempFileCarrySignDownloadUrl(fileName);
// 为了用户体验,你需要告知前端下载的文件名(起始前端就可以确定文件名,这里只是提供一种后端提供文件名的方式)
DownloadFileInfo downloadFileInfo = spFileHelper.getTempFileDownloadInfo(fileName, "2023年第一季度汇总.xlsx");
导入模板下载
数据导入模板我们统一存在 resources/import-template
目录下,使用以下方式获取文件。
@Resource
private SpFileHelper spFileHelper;
@GetMapping("import-template")
public ResponseEntity<byte[]> importTemplate() {
try {
// 获取到模板文件 byte[]
byte[] body = spFileHelper.getResourceByteArray("classpath:import-template/employee.xlsx");
// 使用工具类返回文件流
return SpringHttpUtils.downloadSuccessXlsxResponseEntity(body);
} catch (IOException e) {
logger.error("模板下载失败", e);
}
// 根据自己的情况处理下载失败的返回信息
return SpringHttpUtils.downloadNotFoundResponseEntity();
}
头像上传
员工头像上传
员工头像统一存储在一个目录下,上传头像的功能我们将放在 员工管理
中,员工只有在登录状态下才能上传自己的头像。
// 获取员工头像存储根目录
String storageRootPath = spFileHelper.getEmployeeAvatarStorageRootPath();
// 文件名使用工号 + FileConsts.AVATAR_NAME_SUFFIX 即可
String fileName = ${工号} + FileConsts.AVATAR_NAME_SUFFIX;
自定义员工默认头像
// 获取员工头像存储根目录
String storageRootPath = spFileHelper.getEmployeeAvatarStorageRootPath();
// 文件名使用 FileConsts.DEFAULT_AVATAR_NAME 即可
String fileName = FileConsts.DEFAULT_AVATAR_NAME
头像下载
获取头像的规则如下:
- 头像的访问地址为
${apiBaseUrl.file}/avatar/employee/${工号}.png
,apiBaseUrl.file
为 file 模块的访问地址。 - 如果为多租户系统,那么头像地址后必须添加
sn=${tenantSN}
参数来确认租户。 - 前端可以直接使用 SpEmployeeAvatar 组件来获取头像。
- 也可以使用
useEmployee
hooks 来获取头像地址。
富文本图片签名
富文本上传完图片后返回的原本就是已经签名好的路径,可以将其直接存储到数据库中,在查询富文本内容时后端可以更新签名。
String html = spFileHelper.updateHtmlImageSign("html 内容")
重要
updateHtmlImageSign
方法在使用中是存在限制的,只会把 ?sign=
后面的内容删除掉,并追加上新的签名
这种情况会正常处理
"/files/abc/abc.png?sign=123" ==> "/files/abc/abc.png?sign=456"
这种情况会将 &t=789
删除
"/files/abc/abc.png?sign=123&t=789" ==> "/files/abc/abc.png?sign=456"
这种情况不会处理
/files/abc/abc.png?t=789&sign=123" ==> "/files/abc/abc.png?t=789&sign=123
公共资源文件
公共资源文件为无需鉴权即可访问的文件,其本质就是在原有的文件目录下创建了一个子目录。这个目录下的文件均是无需鉴权就可以访问的文件。
提示
适用场景
类似 webcall 的一些公开的资源文件,如:默认头像、广告贴图等等。
使用方式
注意
当通用的上传功能无法满足您的需求时,也可以使用以下方式自定义上传接口。但一定要使用 fileAuthHelper.isLegal
对文件类型进行安全审查。
业务模块约定一个自己的公共资源子目录,如
webcall-resource
。使用 SpFileHelper 工具类中获取公共资源存储根目录。
// 获取公共资源的存储根目录 String storageRootPath = spFileHelper.getPublicResourceStorageRootPath(); // 拼接为业务专用的根目录 String webcallResourceStorageRootPath = storageRootPath + "webcall-resource/"; // 编写对应的业务上传文件逻辑,假设我们要上传 /webcall-resource/test/test.png String test = webcallResourceAccessRootPath + "test/test.png";
使用 SpFileHelper 工具类中获取公共资源访问根目录。
// 获取公共资源访问根目录 String accessRootPath = spFileHelper.getPublicResourceAccessRootPath(); // 拼接为业务专用的根目录 String webcallResourceAccessRootPath = accessRootPath + "webcall-resource/"; // 这里可以拼接为具体的文件路径返回给前端,假设我们要让前端访问 /webcall-resource/test/test.png String test = webcallResourceAccessRootPath + "test/test.png";
提示
- 您无需处理租户问题,在
getPublicResourceStorageRootPath
和getPublicResourceAccessRootPath
中已经进行了处理。 - 为了方便文件的迁移,推荐数据库中只存储相对路径,使用时再进行拼接。不要将
PublicResourceStorageRootPath
和PublicResourceAccessRootPath
进行入库操作。
封装业务资源目录工具类
为了减少重复代码,我们可以封装一个工具类,将业务公共资源目录的获取封装起来。下面我们以 webcall-resource
为例。
@Component
public class WebcallPublicResourceHelper implements InitializingBean {
@Resource
private SpFileHelper spFileHelper;
private String webcallResourceStorageRootPath;
private String webcallResourceAccessRootPath;
/**
* webcall 专属资源目录
*/
private static final String RESOURCE_PATH = "webcall-resource/";
@Override
public void afterPropertiesSet() throws Exception {
this.webcallResourceStorageRootPath = spFileHelper.getPublicResourceStorageRootPath() + RESOURCE_PATH;
this.webcallResourceAccessRootPath = spFileHelper.getPublicResourceAccessRootPath() + RESOURCE_PATH;
}
public String getWebcallResourceStorageRootPath() {
return this.webcallResourceStorageRootPath;
}
public String getWebcallResourceAccessRootPath() {
return this.webcallResourceAccessRootPath;
}
}