百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT知识 > 正文

快来试试这款一行代码实现多平台文件存储的工具!

liuian 2025-06-08 22:36 49 浏览

大家好,我是 Java陈序员。

文件上传下载,是我们在开发中经常会遇到的需求!

无论是本地存储、还是云存储,我们可以自己封装 API 来实现功能。

今天,给大家介绍一款一行代码实现多平台文件存储的工具,开箱即用!

关注微信公众号:【Java陈序员】,获取开源项目分享、AI副业分享、超200本经典计算机电子书籍等。

项目介绍

x-file-storage —— 一行代码实现将文件存储到本地、FTP、SFTP、WebDAV、OSS 云存储、其它兼容 S3 协议的存储平台。

支持的存储平台:

支持的对象存储:

快速入门

配置

1、引入项目依赖

<dependency>
    <groupId>org.dromara.x-file-storage</groupId>
    <artifactId>x-file-storage-spring</artifactId>
    <version>2.0.0</version>
</dependency>

默认是 SpringBoot 环境,如不是 SpringBoot 环境可使用如下方式初始化。

//配置文件定义存储平台
FileStorageProperties properties = new FileStorageProperties();
properties.setDefaultPlatform("ftp-1");
FtpConfig ftp = new FtpConfig();
ftp.setPlatform("ftp-1");
ftp.setHost("192.168.3.100");
ftp.setPort(2121);
ftp.setUser("root");
ftp.setPassword("123456");
ftp.setDomain("ftp://192.168.3.100:2121/");
ftp.setBasePath("ftp/");
ftp.setStoragePath("/");
properties.setFtp(Collections.singletonList(ftp));

//创建,自定义存储平台、 Client 工厂、切面等功能都有对应的添加方法
FileStorageService service = FileStorageServiceBuilder.create(properties).useDefault().build();

//初始化完毕,开始上传吧
FileInfo fileInfo = service.of(new File("D:\\Desktop\\a.png")).upload();

2、引入对应平台的依赖,如使用阿里云OSS,引入阿里云OSS的依赖,依此类推

 <dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.16.1</version>
</dependency>

3、配置文件中添加配置

dromara:
  x-file-storage: #文件存储配置
    default-platform: huawei-obs-1 #默认使用的存储平台
    thumbnail-suffix: ".min.jpg" #缩略图后缀,例如【.min.jpg】【.png】
    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/ # 基础路径

tips:其他云平台的配置可参考官方文档:https://x-file-storage.xuyanwu.cn/#/

编码

1、在启动类上加上 @EnableFileStorage 注解

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

2、上传文件

// 注入实列
@Autowired
private FileStorageService fileStorageService;

/**
 * 上传文件
 */
@PostMapping("/upload")
public FileInfo upload(MultipartFile file) {
    return fileStorageService.of(file).upload();
}

完整功能

上传文件

直接上传

fileStorageService.of(file).upload();

文件流上传

fileStorageService.of(inputStream).setOriginalFilename("a.jpg").upload();

上传到指定路径下

fileStorageService.of(file)
        .setPath("upload/") // 保存到相对路径下,为了方便管理,不需要可以不写
        .upload();

关联文件参数并上传

fileStorageService.of(file)
        .setObjectId("0")   // 关联对象id,为了方便管理,不需要可以不写
        .setObjectType("0") // 关联对象类型,为了方便管理,不需要可以不写
        .putAttr("role","admin") //保存一些属性,可以在切面、保存上传记录、自定义存储平台等地方获取使用,不需要可以不写
        .putAttr("username","007")
        .upload();

上传到指定的存储平台

fileStorageService.of(file)
        .setPlatform("aliyun-oss-1")    // 使用指定的存储平台
        .upload();

上传图片并进行处理

fileStorageService.of(file)
        .setThumbnailSuffix(".jpg") //指定缩略图后缀,必须是 thumbnailator 支持的图片格式,默认使用全局的
        .setSaveThFilename("thabc") //指定缩略图的保存文件名,注意此文件名不含后缀,默认自动生成
        .image(img -> img.size(1000,1000))  // 将图片大小调整到 1000*1000
        .thumbnail(th -> th.size(200,200))  // 再生成一张 200*200 的缩略图
        .upload();

直接上传 HttpServletRequest

通过直接读取输入流进行上传,可以实现文件不落盘,边读取边上传,速度更快。

需要先在配置文件中开启 multipart 懒加载,不然在 Controller 中拿到输入流是已经被读取过的。

spring.servlet.multipart.resolve-lazily: true

接口实现:

@RestController
public class FileUploadController {

    /**
     * 直接读取 HttpServletRequest 中的文件进行上传,成功返回文件信息
     */
    @PostMapping("/upload-request")
    public FileInfo uploadRequest(HttpServletRequest request) {
        return fileStorageService.of(request).upload();
    }
}

监听上传进度

// 方式一
fileStorageService.of(file).setProgressMonitor(progressSize ->
    System.out.println("已上传:" + progressSize)
).upload();

// 方式二
fileStorageService.of(file).setProgressMonitor((progressSize,allSize) ->
    System.out.println("已上传 " + progressSize + " 总大小" + allSize)
).upload();

// 方式三
fileStorageService.of(file).setProgressMonitor(new ProgressListener() {
    @Override
    public void start() {
    System.out.println("上传开始");
    }

    @Override
    public void progress(long progressSize,long allSize) {
        System.out.println("已上传 " + progressSize + " 总大小" + allSize);
    }

    @Override
    public void finish() {
        System.out.println("上传结束");
    }
}).upload();

下载

获取文件信息

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");

监听下载进度

 // 方式一
fileStorageService.download(fileInfo).setProgressMonitor(progressSize ->
        System.out.println("已下载:" + progressSize)
).file("C:\\a.jpg");
        
// 方式二
fileStorageService.download(fileInfo).setProgressMonitor((progressSize,allSize) ->
        System.out.println("已下载 " + progressSize + " 总大小" + allSize)
).file("C:\\a.jpg");

// 方式三
fileStorageService.download(fileInfo).setProgressMonitor(new ProgressListener() {
    @Override
    public void start() {
        System.out.println("下载开始");
    }

    @Override
    public void progress(long progressSize,long allSize) {
        System.out.println("已下载 " + progressSize + " 总大小" + allSize);
    }

    @Override
    public void finish() {
        System.out.println("下载结束");
    }
}).file("C:\\a.jpg");

判断文件是否存在

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

删除文件

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

最后

推荐的开源项目已经收录到 GitHub 项目,欢迎 Star:

https://github.com/chenyl8848/great-open-source-project

或者访问网站,进行在线浏览:

https://chencoding.top:8090/#/

大家的点赞、收藏和评论都是对作者的支持,如文章对你有帮助还请点赞转发支持下,谢谢!

相关推荐

Javascript中的字符串拼接(js字符串拼接效率)

字符串拼接是所有程序设计语言都需要的操作。当拼接结果较长时,如何保证效率就成为一个很重要的问题。本文介绍的是Javascript中的字符串拼接,希望对你有帮助,一起来看。consticon=&#...

C#-字符串常用方法(2) 104(c#字符串处理函数)

IndexOf()查找某字符或字符串在要查找字符串中第一次出现的下标,与数组相同从零开始,如果没有找到对应的数据,则返回-1LastIndexOf()查找某字符或字符串在要查找字符串中最后一次出现...

C#拼接字符串及简单性能比较(c# 如何高效拼接字符串)

在C#编程中拼接字符串应该是最常见的场景之一,假如现在有几个变量需要转换成字符串并按格式拼接,常用的几种方法:inta=1;charb='c';doublec=1...

C# 字符串连接方式有哪些?各自有什么特点?

在C#中,有几种方式可以连接字符串,每种方式都有其特点和适用场景。以下是常见的几种方式:使用连接符"+":使用加号"+"可以简单地将两个字符串连接起来。这种方式简...

字符串也可以比较大小?C语言的strcmp函数

在C语言中,因为字符串没有被封装成对象,所以直接用==、!=比较。C语言提供了一个用于字符串比较的函数类型strcmp。注意,字符串和字符完全不同,因为不同的编译器实现问题,返回值的情况也有所变化...

JavaScript 常用功能总结(javascript的功能)

编吐血整理加上翻译,太辛苦了~求赞!本文主要总结了JavaScript常用功能总结,如一些常用的额JS对象,基本数据结构,功能函数等,还有一些常用的设计模式。目录:众所周知,JavaScript是...

C++20 新特性(16):三路比较运算符()一统浆糊

C++语言的比较操作符有很多,包括:==、!=、>、>=、<、<=等,如果要实现对象的大小比较功能,需要实现这些函数,比较繁琐,而且如果实现不好,这些...

C++中重载运算符的使用(c++重载操作符)

C++中重载运算符的使用:用户定义的类型,如:字符串,日期,复数,联合体以及文件常常重载二元+操作符以实现对象的连接,附加或合并机制。但是要正确实现+操作符会给设计,实现和性能带来一定的挑战。...

《学习C++》基本概念之标识符(c++标识符的定义)

标识符(identifier)是C++基本词法单元(即令牌token)之一,由字母(letter)、数字(digit)、下划线(underscore)组成,但是首字符必须为字母或下换线。标识符的用途主...

Java判断Integer相等-应该这么这样用

先看下这段代码,然后猜下结果:Integeri1=50;Integeri2=50;Integeri3=128;Integeri4=128;System.out.prin...

C语言模拟实现字符串操作函数(c语言模拟实现字符串操作函数是什么)

1.strlenstrlen是用来求字符串长度的函数,字符串长度就是字符串中包含的字符的个数,但是不包含字符串结尾的‘\0’实现strlen有三种方法:(1)定义一个计数器size_t ...

C++ 知识小结(c++知识总结)

C语言typedef与#define比较https://www.runoob.com/note/24230野指针和悬空指针区别?野指针:指的是没有被初始化过的指针。解决方法:指针变量未及时...

Python字符串比较的隐藏法则:Unicode对决、内存地址暗战!

字符串比较的底层规则核心原理:字符逐个对比,基于Unicode值一决胜负!#规则演示:从首字符开始逐位比较print("apple">"app")...

在C++中,如何避免出现Bug?(c++防止闪退)

C++中的主要问题之一是存在大量行为未定义或对程序员来说意外的构造。我们在使用静态分析器检查各种项目时经常会遇到这些问题。但正如我们所知,最佳做法是在编译阶段尽早检测错误。让我们来看看现代C++中的一...

harmony-utils之StrUtil,字符串工具类

harmony-utils简介与说明harmony-utils一款功能丰富且极易上手的HarmonyOS工具库,借助众多实用工具类,致力于助力开发者迅速构建鸿蒙应用。其封装的工具涵盖了APP、设备...