编辑
2025-08-03
实用工具
00

目录

简介
使用
依赖
yml
启动类
上传
获取
删除
判断
复制
移动
下载
COS迁移到OSS

简介

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

使用

使用阿里云为例

依赖

xml
<!-- 引入本项目 --> <dependency> <groupId>org.dromara.x-file-storage</groupId> <artifactId>x-file-storage-spring</artifactId> <version>2.3.0</version> </dependency> <!-- 引入 阿里云 OSS SDK,如果使用其它存储平台,就引入对应的 SDK --> <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>3.16.1</version> </dependency>

yml

yml
dromara: x-file-storage: #文件存储配置 default-platform: aliyun-oss-1 #默认使用的存储平台 aliyun-oss: - platform: aliyun-oss-1 # 存储平台标识 enable-storage: true # 启用存储 access-key: ?? secret-key: ?? end-point: ?? bucket-name: ?? domain: ?? # 访问域名,注意“/”结尾,例如:https://abc.oss-cn-shanghai.aliyuncs.com/ base-path: test/ # 基础路径

启动类

java
@EnableFileStorage @SpringBootApplication public class SpringFileStorageTestApplication { public static void main(String[] args) { SpringApplication.run(SpringFileStorageTestApplication.class,args); } }

上传

java
@RestController public class FileDetailController { @Autowired private FileStorageService fileStorageService;//注入实列 /** * 上传文件 */ @PostMapping("/upload") public FileInfo upload(MultipartFile file) { //只需要这一行代码即可上传成功 return fileStorageService.of(file).upload(); } /** * 上传文件,成功返回文件 url */ @PostMapping("/upload2") public String upload2(MultipartFile file) { FileInfo fileInfo = fileStorageService.of(file) .setPath("upload/") //保存到相对路径下,为了方便管理,不需要可以不写 .setSaveFilename("image.jpg") //设置保存的文件名,不需要可以不写,会随机生成 .setObjectId("0") //关联对象id,为了方便管理,不需要可以不写 .setObjectType("0") //关联对象类型,为了方便管理,不需要可以不写 .putAttr("role","admin") //保存一些属性,可以在切面、保存上传记录、自定义存储平台等地方获取使用,不需要可以不写 .upload(); //将文件上传到对应地方 return fileInfo == null ? "上传失败!" : fileInfo.getUrl(); } /** * 上传图片,成功返回文件信息 * 图片处理使用的是 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(); } /** * 上传文件到指定存储平台,成功返回文件信息 */ @PostMapping("/upload-platform") public FileInfo uploadPlatform(MultipartFile file) { return fileStorageService.of(file) .setPlatform("aliyun-oss-1") //使用指定的存储平台 .upload(); } /** * 直接读取 HttpServletRequest 中的文件进行上传,成功返回文件信息 * 使用这种方式有些注意事项,请查看文档 基础功能-上传 章节 */ @PostMapping("/upload-request") public FileInfo uploadPlatform(HttpServletRequest request) { return fileStorageService.of(request).upload(); } //还有监听上传进度,手动分片上传 }

获取

java
//通过 FileInfo 获取文件信息 RemoteFileInfo info = fileStorageService.getFile(fileInfo); //通过 FileInfo 获取缩略图文件信息 RemoteFileInfo info2 = fileStorageService.getThFile(fileInfo); //将结果转成 FileInfo 方便进行其它操作 FileInfo fileInfo2 = info.toFileInfo(); //将缩略图文件信息也 set 到 FileInfo 中 info2.toFileInfoTh(fileInfo2); //自行传入 path 及 filename 获取文件信息 RemoteFileInfo info3 = fileStorageService.getFile().setPath("test/").setFilename("123.jpg").getFile(); Assert.notNull(info3, "文件不存在"); log.info("获取文件结果:{}", info3); //文件元数据 MapProxy metadata = info3.getKebabCaseInsensitiveMetadata(); //文件用户元数据 MapProxy userMetadata = info3.getKebabCaseInsensitiveUserMetadata(); //获取原始文件信息,这里以阿里云为例 OSSObject ossObject = info3.getOriginalAliyunOssObject();

删除

java
//获取文件信息 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");

判断

java
//获取文件信息 FileInfo fileInfo = fileStorageService.getFileInfoByUrl("https://file.abc.com/test/a.jpg"); //判断文件是否存在 boolean exists = fileStorageService.exists(fileInfo); //直接通过文件信息中的 url 判断文件是否存在,省去手动查询文件信息记录的过程 boolean exists2 = fileStorageService.exists("https://file.abc.com/test/a.jpg"); ****

复制

java
// 上传源文件 FileInfo fileInfo = fileStorageService.of(new File("D:\\Desktop\\a.png")).thumbnail().upload(); // 复制到 copy 这个路径下(同存储平台复制) FileInfo destFileInfo = fileStorageService.copy(fileInfo) .setPath("copy/") .copy(); //复制到同路径下不同文件名(同存储平台复制) FileInfo destFileInfo = fileStorageService.copy(fileInfo) .setFilename("aaaCopy." + FileNameUtil.extName(fileInfo.getFilename())) .setThFilename("aaaCopy.min." + FileNameUtil.extName(fileInfo.getThFilename())) .copy(); //复制到其它存储平台(跨存储平台复制) FileInfo destFileInfo = fileStorageService.copy(fileInfo) .setPlatform("local-plus-1") .setProgressListener((progressSize, allSize) -> log.info("文件复制进度:{} {}%", progressSize, progressSize * 100 / allSize)) .copy(); //强制使用跨存储平台复制 FileInfo destFileInfo = fileStorageService.copy(fileInfo) .setCopyMode(Constant.CopyMode.CROSS) .setPath("copy/") .copy(); //是否支持同存储平台复制 boolean supportSameCopy = fileStorageService.isSupportSameCopy("aliyun-oss-1");

移动

java
// 上传源文件 FileInfo fileInfo = fileStorageService.of(new File("D:\\Desktop\\a.png")).thumbnail().upload(); // 移动到 move 这个路径下(同存储平台移动) FileInfo destFileInfo = fileStorageService.move(fileInfo) .setPath("move/") .move(); //移动到同路径下不同文件名(同存储平台移动) FileInfo destFileInfo = fileStorageService.move(fileInfo) .setFilename("aaaMove." + FileNameUtil.extName(fileInfo.getFilename())) .setThFilename("aaaMove.min." + FileNameUtil.extName(fileInfo.getThFilename())) .move(); //移动到其它存储平台(跨存储平台移动) FileInfo destFileInfo = fileStorageService.move(fileInfo) .setPlatform("local-plus-1") .setProgressListener((progressSize, allSize) -> log.info("文件移动进度:{} {}%", progressSize, progressSize * 100 / allSize)) .move(); //强制使用跨存储平台移动 FileInfo destFileInfo = fileStorageService.move(fileInfo) .setMoveMode(Constant.MoveMode.CROSS) .setPath("move/") .move(); //是否支持同存储平台移动 boolean supportSameMove = fileStorageService.isSupportSameMove("aliyun-oss-1");

下载

java
// 获取文件信息 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");

COS迁移到OSS

java
@Slf4j @SpringBootTest class FileStorageServiceTransferTest { @Autowired private FileStorageService fileStorageService; /** * 从存储平台读取迁移 */ @Test public void transferByPlatform() { // 源存储平台 String fromPlatform = "cos-1"; // 目标存储平台 String toPlatform = "oss-1"; // 要迁移的路径 String path = ""; // 开始迁移 transfer(fromPlatform, toPlatform, path); } /** * 使用数据库迁移文件 */ public void transfer(String fromPlatform, String toPlatform, String path) { // 例举出当前路径下所有目录及文件 ListFilesResult result = fileStorageService .listFiles() .setPlatform(fromPlatform) .setPath(path) .listFiles(); // 递归迁移所有子目录 for (RemoteDirInfo dir : result.getDirList()) { transfer(fromPlatform, toPlatform, path + dir.getName() + "/"); } // 迁移当前路径下所有文件 for (RemoteFileInfo remoteFileInfo : result.getFileList()) { // 转换成 FileInfo,注意 createTime 、metadata 及 userMetadata 可能需要自行处理,详情查看方法源码注释 // 同时每个存储平台的 ACL 也不一样,也需要自行处理 FileInfo fileInfo = remoteFileInfo.toFileInfo(); // 这里仅保留需要的 metadata ,例如: Map<String, String> fromMetadata = new KebabCaseInsensitiveMap<>(fileInfo.getMetadata()); Map<String, String> toMetadata = new HashMap<>(); if (fromMetadata.containsKey(Constant.Metadata.CONTENT_TYPE)) { toMetadata.put(Constant.Metadata.CONTENT_TYPE, fromMetadata.get(Constant.Metadata.CONTENT_TYPE)); } if (fromMetadata.containsKey(Constant.Metadata.CONTENT_LENGTH)) { toMetadata.put(Constant.Metadata.CONTENT_LENGTH, fromMetadata.get(Constant.Metadata.CONTENT_LENGTH)); } if (fromMetadata.containsKey(Constant.Metadata.CONTENT_DISPOSITION)) { toMetadata.put( Constant.Metadata.CONTENT_DISPOSITION, fromMetadata.get(Constant.Metadata.CONTENT_DISPOSITION)); } fileInfo.setMetadata(toMetadata); // 使用复制文件,会保留旧文件,推荐 fileStorageService .move(fileInfo) .setPlatform(toPlatform) .setNotSupportMetadataThrowException(true) // 不支持元数据时抛出异常 .setNotSupportAclThrowException(true) // 不支持 ACL 时抛出异常 .setProgressListener((progressSize, allSize) -> log.info( "文件 {}/{} 迁移进度:{} {}%", fileInfo.getPath(), fileInfo.getFilename(), progressSize, progressSize * 100 / allSize)) .move(); } } }

本文作者:Weee

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!