文件处理
大约 8 分钟约 2435 字
文件管理
功能描述
文件管理分为两部分
- blade-file 提供了文件类型检测、元数据检测、文件校验、解压缩能力的集成和封装,简化文件相关操作。
- blade-file-spring-boot-starter 是基于 SpringBoot 的 OSS 启动器,提供了 S3 OSS 集成和封装,支持常用上传、下载、分片上传、分片下载、文件夹上传、文件夹删除等常用功能,此外提供配置式内存限制,避免大文件操作 OOM 问题。
更新记录
v3.0.1
- 压缩文件:zip、tar、gz、rar、7z 等
- 图片:jpeg、png、webp、ico 等
- 文档:txt、doc、xls、ppt、pdf 等
v3.0.3
- 获取 Oss 文件元信息:Blade Issue 44
v3.1.2
- 支持 url 校验、url 下载、 url转存 Blade Issue 47
GAV 坐标
Gradle
implementation(commonLibs.blade.file)
implementation(commonLibs.blade.file.spring.boot.starter)Maven
<dependency>
<groupId>team.aikero.blade</groupId>
<artifactId>blade-file</artifactId>
</dependency>
<dependency>
<groupId>team.aikero.blade</groupId>
<artifactId>blade-file-spring-boot-starter</artifactId>
</dependency>快速集成
- 选择合适的 GAV,在项目中添加依赖。
implementation(commonLibs.blade.file)
implementation(commonLibs.blade.file.spring.boot.starter)- 在项目的
application.yaml中添加下方代码。
spring:
config:
import:
- optional:nacos:blade-config <--- 在项目配置之前引入此配置
- optional:nacos:${spring.application.name}详细说明
配置示例
默认情况下,blade-config 提供全局公共配置
若有特殊定制需求可以在application.yml文件中,可以进行以下配置:
blade:
oss:
# 是否启用 oss
enabled: true
config:
default:
# S3 兼容对象存储类型 aws、aliyun、minio
type: aliyun
# 自定义域名
domain: https://jv-arsenal.oss-cn-hangzhou.aliyuncs.com
# 对象存储服务的URL(S3 Style)
endpoint: https://oss-cn-hangzhou.aliyuncs.com
# 存储区域
region: oss-cn-hangzhou
# 存储空间名称
bucket: jv
# 访问密钥ID
access-key:
# 访问密钥Key
secret-key:
upload:
# 分块上传,每个分块的大小,当文件超出此大小时进行分片上传,默认 10M
multipartSize: 10485760
# 最大内存占用,此设置影响文件处理所使用的内存占用,默认为 100M
# 间接影响:分块上传最大并发数、文件夹上传最大并发数
maxUsageMemory: 104857600代码示例
基础文件操作
主要类有:
- MediaType: 文件类型定义
- FileMetadata: 文件元数据定义,针对标准类型文件提取通用协议元信息,提供类型安全api
- FileInfos: 文件类型探测、文件元数据提取
- FileCompressions: 压缩文件解压
- FileValidations: 通过文件元数据进行文件校验
文件操作均提供 Path(NIO)、File(BIO) 两种类型支持,示例代码使用 File 进行演示。
java
File testFile = new File("test.zip");
// 检测文件类型
MediaType type = FileInfos.type(testFile);
// 输出为 MediaType.ZIP
type.isArchive(); // true
type.isImage(); // false
// 检测文件元数据
FileMetadata metadata = FileInfos.metadata(testFile);
metadata.getName() // test.zip
metadata.getMediaType() // MediaType.ZIP
metadata.getSize() // n
// 检测文件元数据 (压缩文件递归)
FileMetadata metadata = FileInfos.metadata(testFile, true);
ArchiveMetadata archiveMetadata = (ArchiveMetadata) metadata;
archiveMetadata.getName() // test.zip
archiveMetadata.getMediaType() // MediaType.ZIP
archiveMetadata.getSize() // n
archiveMetadata.getTempLocation() // 解压临时路径,1h 自动清理
archiveMetadata.getArchiveItems() // 归档项元数据集合,ArchiveItemMetadata 类型
archiveMetadata.getItems() // 归档项元数据集合,文件原始类型
// 解压到指定临时目录
Path tempPath = FileCompressions.decompress(testFile, "demo/");
// 返回:/tmp/demo
// 解压到指定目录,并记录每个文件路径
Map<String, Path> pathMapping = new HashMap<>();
Path tempPath = FileCompressions.decompress(testFile, "demo/", pathMapping);
// 文件校验,验证文件大小大于 100
ValidationResult<FileMetadata> result = FileValidations.validate(Validators.fileSize(100, Condition.GE), metadata);
ValidationResult<FileMetadata> result = validated.get(0);
if (result.isSuccess()) {
}
if (result.isFailure()) {
Throwable throwable = result.exceptionOrNull();
FileValidationException exception = (FileValidationException) throwable;
FileMetadata metadata = exception.getMetadata();
}kotlin
val testFile = File("test.zip")
// 检测文件类型
val type = testFile.type()
// 输出为 MediaType.ZIP
type.isArchive(); // true
type.isImage(); // false
// 检测文件元数据
val metadata = testFile.metadata()
metadata.getName() // test.zip
metadata.getMediaType() // MediaType.ZIP
metadata.getSize() // n
// 检测文件元数据 (压缩文件递归)
val metadata = testFile.metadata(true)
val archiveMetadata = metadata as ArchiveMetadata
archiveMetadata.getName() // test.zip
archiveMetadata.getMediaType() // MediaType.ZIP
archiveMetadata.getSize() // n
archiveMetadata.getTempLocation() // 解压临时路径,1h 自动清理
archiveMetadata.getArchiveItems() // 归档项元数据集合,ArchiveItemMetadata 类型
archiveMetadata.getItems() // 归档项元数据集合,文件原始类型
// 解压到指定临时目录
val tempPath = testFile.decompress("demo/")
// 返回:/tmp/demo
// 解压到指定目录,并记录每个文件路径
val pathMapping = mutableMapOf<String, Path>()
val tempPath = testFile.decompress("demo/", pathMapping)
// 文件校验,验证文件大小大于 100
val result = FileValidations.validate(Validators.fileSize(100, Condition.GE), metadata)
// 校验结果处理
result.onSuccess {
val itemMetadata = it
log.info { "文件校验成功:${itemMetadata.name} ${it.message} ${itemMetadata.source}" }
}
result.onValidationFailure {
val itemMetadata = ex.metadata
log.info { "文件校验失败:${itemMetadata.name} ${it.message} ${itemMetadata.source}" }
}OSS 文件操作
OSS 操作由 aws-s3-kotlin 封装提供,支持文件上传、下载、删除以及分块上传、多客户端切换等功能。通过可配置的内存管理机制,有效降低 OOM 风险。
注意
涉及输入流的 api 时,应注意流始终由调用方决定何时关闭。
java
File testFile = new File("test.png")
File testDir = new File("test")
InputStream stream = new FileInputStream("test.png")
// 最简使用方式,当文件大于 最小分块配置 时,自动启用分块上传功能。
ossTemplate.upload(testFile.getName(), testFile)
// 指定 bucket,缺省值为 ossClient 配置 bucket
ossTemplate.upload("bucket1", "custom key", testFile)
// 当多次上传同一个 key 时,将覆盖原文件,文件链接不变。若期望保持原文件不变,修改 key 即可。
ossTemplate.upload("custom key", testFile)
// 使用流时,传入内容长度,有助于自动启用分块上传功能
ossTemplate.upload("custom key", stream, file.length())
// 使用流时,对于未知长度的上传,进行流式上传(!超大文件直传有断连风险)
ossTemplate.upload("custom key", stream)
// 使用流时,对于未知长度的上传,期望强制启用分块上传(!小文件上传速度可能变慢)
ossTemplate.multipartUpload("custom key", stream)
// 上传文件夹
List<String> urls = ossTemplate.uploadFolder("custom key", testDir)
// 文件下载,返回临时文件 1h 自动清理
File download = ossTemplate.download("custom key")
// 判断文件是否存在
Boolean exist = ossTemplate.exist("custom key")
// 一次移除文件夹内 1000 项, 若文件夹内数量大于 1000 时,阿里云 xml 会解析失败。若有需求,多次调用直至返回 0。
Int num = ossTemplate.removeFolder("custom key")
// 删除单个文件
Boolean success = ossTemplate.remove("custom key")
// 切换客户端
DynamicOssHolder.push("ali")
// ... do something
// 恢复默认客户端
DynamicOssHolder.poll()
// 3.0.3 新增文件信息获取接口
// 获取 oss 元数据(存储于 oss header,包含上传时服务、环境、操作人、原文件名、文件大小,任意字段均可能为空,取决于上传时自动注入能否获取到相关信息)
ObjectInfo info = ossTemplate.info("custom key")
// 获取文件元数据(解析文件数据,提取文件元信息)
FileMetadata fileInfo = ossTemplate.fileInfo("custom key")
// 3.1.2 新增文件信息获取接口
// URI 转存,若存在于 oss 中,则返回已存在文件信息
ossTemplate.transferFrom(new URI("https://dev-docs.tiangong.tech/assets/use_tech-BNv4kln5.png"))
// URI 下载,若存在于 oss,则通过 oss 下载(内网情况下减少流量费用)
ossTemplate.donwnload(new URI("https://dev-docs.tiangong.tech/assets/use_tech-BNv4kln5.png"))kotlin
val testFile = File("test.png")
val testDir = File("test")
val stream = FileInputStream("test.png")
// 最简使用方式,当文件大于 最小分块配置 时,自动启用分块上传功能。
ossTemplate.upload(testFile.name, testFile)
// 指定 bucket,缺省值为 ossClient 配置 bucket
ossTemplate.upload("bucket1", "custom key", testFile)
// 当多次上传同一个 key 时,将覆盖原文件,文件链接不变。若期望保持原文件不变,修改 key 即可。
ossTemplate.upload("custom key", testFile)
// 使用流时,传入内容长度,有助于自动启用分块上传功能
ossTemplate.upload("custom key", stream, file.length())
// 使用流时,对于未知长度的上传,进行流式上传(!超大文件直传有断连风险)
ossTemplate.upload("custom key", stream)
// 使用流时,对于未知长度的上传,期望强制启用分块上传(!小文件上传速度可能变慢)
ossTemplate.multipartUpload("custom key", stream)
// 上传文件夹
val urls = ossTemplate.uploadFolder("custom key", testDir)
// 文件下载,返回临时文件 1h 自动清理
val download = ossTemplate.download("custom key")
// 判断文件是否存在
val exist = ossTemplate.exist("custom key")
// 一次移除文件夹内 1000 项, 若文件夹内数量大于 1000 时,阿里云 xml 会解析失败。若有需求,多次调用直至返回 0。
val num = ossTemplate.removeFolder("custom key")
// 删除单个文件
val success = ossTemplate.remove("custom key")
// 切换客户端
DynamicOssHolder.push("ali")
// ... do something
// 恢复默认客户端
DynamicOssHolder.poll()
DynamicOssHolder.use("ali") {
// ... do something
}
// 3.0.3 新增文件信息获取接口
// 获取 oss 元数据(存储于 oss header,包含上传时服务、环境、操作人、原文件名、文件大小,任意字段均可能为空,取决于上传时自动注入能否获取到相关信息)
val info = ossTemplate.info(key = "custom key")
// 获取文件元数据(解析文件数据,提取文件元信息)
val fileInfo = ossTemplate.fileInfo(key = "custom key")
// 3.1.2 新增文件信息获取接口
// URI 转存,若存在于 oss 中,则返回已存在文件信息
ossTemplate.transferFrom(URI("https://dev-docs.tiangong.tech/assets/use_tech-BNv4kln5.png"))
// URI 下载,若存在于 oss,则通过 oss 下载(内网情况下减少流量费用)
ossTemplate.donwnload(URI("https://dev-docs.tiangong.tech/assets/use_tech-BNv4kln5.png"))实现方案
基础文件操作
文件元数据检测是基于通用协议,在文件固定位置提取的公共信息。
这些信息受限于文件格式提供者、各类编辑工具对于各类协议的定义的支持和实现,由于文件种类繁多,使解析元数据处理非常复杂。
本次使用 Tika 作为文件元数据解析工具,支持多种文件格式检测,通过对 Tika 进一步封装和 API 定义,简化常用业务场景的使用和校验需求。
OSS 文件操作
OSS 操作封装 aws-s3-kotlin 提供简化 API,通过协程优化结构化并发,提升内存管理和并发管理能力。
