Spring File Storage的详细文档

快速入门

  1. 配置

pom.xml引入依赖

<dependencies>
    <!-- spring-file-storage 必须要引入 -->
    <dependency>
        <groupId>cn.xuyanwu</groupId>
        <artifactId>spring-file-storage</artifactId>
        <version>0.7.0</version>
    </dependency>

    <!-- 华为云 OBS 不使用的情况下可以不引入 -->
    <dependency>
        <groupId>com.huaweicloud</groupId>
        <artifactId>esdk-obs-java</artifactId>
        <version>3.22.3.1</version>
    </dependency>

    <!-- 阿里云 OSS 不使用的情况下可以不引入 -->
    <dependency>
        <groupId>com.aliyun.oss</groupId>
        <artifactId>aliyun-sdk-oss</artifactId>
        <version>3.15.1</version>
    </dependency>

    <!-- 七牛云 Kodo 不使用的情况下可以不引入 -->
    <dependency>
        <groupId>com.qiniu</groupId>
        <artifactId>qiniu-java-sdk</artifactId>
        <version>7.11.0</version>
    </dependency>

    <!-- 腾讯云 COS 不使用的情况下可以不引入 -->
    <dependency>
        <groupId>com.qcloud</groupId>
        <artifactId>cos_api</artifactId>
        <version>5.6.98</version>
    </dependency>

    <!-- 百度云 BOS 不使用的情况下可以不引入 -->
    <dependency>
        <groupId>com.baidubce</groupId>
        <artifactId>bce-java-sdk</artifactId>
        <version>0.10.218</version>
    </dependency>

    <!-- 又拍云 USS 不使用的情况下可以不引入 -->
    <dependency>
        <groupId>com.upyun</groupId>
        <artifactId>java-sdk</artifactId>
        <version>4.2.3</version>
    </dependency>

    <!-- MinIO 不使用的情况下可以不引入 -->
    <dependency>
        <groupId>io.minio</groupId>
        <artifactId>minio</artifactId>
        <version>8.4.3</version>
    </dependency>

    <!-- AWS S3 不使用的情况下可以不引入 -->
    <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-java-sdk-s3</artifactId>
        <version>1.12.272</version>
    </dependency>

    <!-- FTP 不使用的情况下可以不引入 -->
    <dependency>
        <groupId>commons-net</groupId>
        <artifactId>commons-net</artifactId>
        <version>3.8.0</version>
    </dependency>

    <!-- SFTP 不使用的情况下可以不引入 -->
    <dependency>
        <groupId>com.jcraft</groupId>
        <artifactId>jsch</artifactId>
        <version>0.1.55</version>
    </dependency>

    <!--糊涂工具类扩展,如果要使用 FTP、SFTP 则必须引入,否则不用引入-->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-extra</artifactId>
        <version>5.8.5</version>
    </dependency>

    <!-- WebDAV 不使用的情况下可以不引入 -->
    <dependency>
        <groupId>com.github.lookfirst</groupId>
        <artifactId>sardine</artifactId>
        <version>5.10</version>
    </dependency>

    <!-- 谷歌云 Google Cloud Storage-->
    <dependency>
        <groupId>com.google.cloud</groupId>
        <artifactId>google-cloud-storage</artifactId>
        <version>2.14.0</version>
    </dependency>

    <!--因 guava 存在较多冲突版本导致谷歌云存储无法使用,故引入独立版本-->
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>31.1-jre</version>
    </dependency>
    
</dependencies>

application.yml配置文件中添加以下相关配置(不使用的平台可以不配置)

spring:
  file-storage: #文件存储配置
    default-platform: local-1 #默认使用的存储平台
    thumbnail-suffix: ".min.jpg" #缩略图后缀,例如【.min.jpg】【.png】
    local: # 本地存储(不推荐使用),不使用的情况下可以不写
      - platform: local-1 # 存储平台标识
        enable-storage: true  #启用存储
        enable-access: true #启用访问(线上请使用 Nginx 配置,效率更高)
        domain: "" # 访问域名,例如:“http://127.0.0.1:8030/test/file/”,注意后面要和 path-patterns 保持一致,“/”结尾,本地存储建议使用相对路径,方便后期更换域名
        base-path: D:/Temp/test/ # 存储地址
        path-patterns: /test/file/** # 访问路径,开启 enable-access 后,通过此路径可以访问到上传的文件
    local-plus: # 本地存储升级版,不使用的情况下可以不写
      - platform: local-plus-1 # 存储平台标识
        enable-storage: true  #启用存储
        enable-access: true #启用访问(线上请使用 Nginx 配置,效率更高)
        domain: "" # 访问域名,例如:“http://127.0.0.1:8030/”,注意后面要和 path-patterns 保持一致,“/”结尾,本地存储建议使用相对路径,方便后期更换域名
        base-path: local-plus/ # 基础路径
        path-patterns: /** # 访问路径
        storage-path: D:/Temp/ # 存储路径
    huawei-obs: # 华为云 OBS ,不使用的情况下可以不写
      - platform: huawei-obs-1 # 存储平台标识
        enable-storage: false  # 启用存储
        access-key: ??
        secret-key: ??
        end-point: ??
        bucket-name: ??
        domain: ?? # 访问域名,注意“/”结尾,例如:http://abc.obs.com/
        base-path: hy/ # 基础路径
    aliyun-oss: # 阿里云 OSS ,不使用的情况下可以不写
      - platform: aliyun-oss-1 # 存储平台标识
        enable-storage: false  # 启用存储
        access-key: ??
        secret-key: ??
        end-point: ??
        bucket-name: ??
        domain: ?? # 访问域名,注意“/”结尾,例如:https://abc.oss-cn-shanghai.aliyuncs.com/
        base-path: hy/ # 基础路径
    qiniu-kodo: # 七牛云 kodo ,不使用的情况下可以不写
      - platform: qiniu-kodo-1 # 存储平台标识
        enable-storage: false  # 启用存储
        access-key: ??
        secret-key: ??
        bucket-name: ??
        domain: ?? # 访问域名,注意“/”结尾,例如:http://abc.hn-bkt.clouddn.com/
        base-path: base/ # 基础路径
    tencent-cos: # 腾讯云 COS
      - platform: tencent-cos-1 # 存储平台标识
        enable-storage: true  # 启用存储
        secret-id: ??
        secret-key: ??
        region: ?? #存仓库所在地域
        bucket-name: ??
        domain: ?? # 访问域名,注意“/”结尾,例如:https://abc.cos.ap-nanjing.myqcloud.com/
        base-path: hy/ # 基础路径
    baidu-bos: # 百度云 BOS
      - platform: baidu-bos-1 # 存储平台标识
        enable-storage: true  # 启用存储
        access-key: ??
        secret-key: ??
        end-point: ?? # 例如 abc.fsh.bcebos.com
        bucket-name: ??
        domain: ?? # 访问域名,注意“/”结尾,例如:https://abc.fsh.bcebos.com/abc/
        base-path: hy/ # 基础路径
    upyun-uss: # 又拍云 USS
      - platform: upyun-uss-1 # 存储平台标识
        enable-storage: true  # 启用存储
        username: ??
        password: ??
        bucket-name: ??
        domain: ?? # 访问域名,注意“/”结尾,例如:http://abc.test.upcdn.net/
        base-path: hy/ # 基础路径
    minio: # MinIO,由于 MinIO SDK 支持 AWS S3,其它兼容 AWS S3 协议的存储平台也都可配置在这里
      - platform: minio-1 # 存储平台标识
        enable-storage: true  # 启用存储
        access-key: ??
        secret-key: ??
        end-point: ??
        bucket-name: ??
        domain: ?? # 访问域名,注意“/”结尾,例如:http://minio.abc.com/abc/
        base-path: hy/ # 基础路径
    aws-s3: # AWS S3,其它兼容 AWS S3 协议的存储平台也都可配置在这里
      - platform: aws-s3-1 # 存储平台标识
        enable-storage: true  # 启用存储
        access-key: ??
        secret-key: ??
        region: ?? # 与 end-point 参数至少填一个
        end-point: ?? # 与 region 参数至少填一个
        bucket-name: ??
        domain: ?? # 访问域名,注意“/”结尾,例如:https://abc.hn-bkt.clouddn.com/
        base-path: s3/ # 基础路径
    ftp: # FTP
      - platform: ftp-1 # 存储平台标识
        enable-storage: true  # 启用存储
        host: ?? # 主机,例如:192.168.1.105
        port: 21 # 端口,默认21
        user: anonymous # 用户名,默认 anonymous(匿名)
        password: "" # 密码,默认空
        domain: ?? # 访问域名,注意“/”结尾,例如:ftp://192.168.1.105/
        base-path: ftp/ # 基础路径
        storage-path: /www/wwwroot/file.abc.com/ # 存储路径,可以配合 Nginx 实现访问,注意“/”结尾,默认“/”
    sftp: # SFTP
      - platform: sftp-1 # 存储平台标识
        enable-storage: true  # 启用存储
        host: ?? # 主机,例如:192.168.1.105
        port: 22 # 端口,默认22
        user: root # 用户名
        password: ?? # 密码或私钥密码
        private-key-path: ?? # 私钥路径,兼容Spring的ClassPath路径、文件路径、HTTP路径等,例如:classpath:id_rsa_2048
        domain: ?? # 访问域名,注意“/”结尾,例如:https://file.abc.com/
        base-path: sftp/ # 基础路径
        storage-path: /www/wwwroot/file.abc.com/ # 存储路径,可以配合 Nginx 实现访问,注意“/”结尾,默认“/”
    webdav: # WebDAV
      - platform: webdav-1 # 存储平台标识
        enable-storage: true  # 启用存储
        server: ?? # 服务器地址,例如:http://192.168.1.105:8405/
        user: ?? # 用户名
        password: ?? # 密码
        domain: ?? # 访问域名,注意“/”结尾,例如:https://file.abc.com/
        base-path: webdav/ # 基础路径
        storage-path: / # 存储路径,可以配合 Nginx 实现访问,注意“/”结尾,默认“/”
    google-cloud: # 谷歌云存储
      - platform: google-1 # 存储平台标识
        enable-storage: true  # 启用存储
        project-id: ?? # 项目 id
        bucket-name: ??
        credentials-path: file:/deploy/example-key.json # 授权 key json 路径,兼容Spring的ClassPath路径、文件路径、HTTP路径等
        domain: ?? # 访问域名,注意“/”结尾,例如:https://storage.googleapis.com/test-bucket/
        base-path: hy/ # 基础路径

注意配置每个平台前面都有个-号,通过以下方式可以配置多个

local:
  - platform: local-1 # 存储平台标识
    enable-storage: true
    enable-access: true
    domain: ""
    base-path: D:/Temp/test/
    path-patterns: /test/file/**
  - platform: local-2 # 存储平台标识,注意这里不能重复
    enable-storage: true
    enable-access: true
    domain: ""
    base-path: D:/Temp/test2/
    path-patterns: /test2/file/**

2、编码

在启动类上加上@EnableFileStorage注解

@EnableFileStorage
@SpringBootApplication
public class SpringFileStorageTestApplication {

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

3、开始使用


@RestController
public class FileDetailController {

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

    /**
     * 上传文件,成功返回文件 url
     */
    @PostMapping("/upload")
    public String upload(MultipartFile file) {
        FileInfo fileInfo = fileStorageService.of(file)
                .setPath("upload/") //保存到相对路径下,为了方便管理,不需要可以不写
                .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();
    }
}

如果想使用删除、下载等功能,请阅读 保存上传记录 章节

基础功能

上传

多种上传方式

of方法有很多个重载方法,支持 File、MultipartFile、byte[]、InputStream、URL、URI、String

// 直接上传
fileStorageService.of(file).upload();

// 如果要用 InputStream、URL、URI、String 等方式上传,暂时无法获取 originalFilename 属性,最好手动设置
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();

// 对图片进行处理并上传,有多个重载方法。图片处理使用的是 https://github.com/coobird/thumbnailator
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();

// 其它更多方法以实际 API 为准

保存上传记录

如果还想使用除了保存文件之外的其它功能,例如删除、下载文件,还需要实现 FileRecorder 这个接口,把文件信息保存到数据库中。

/**
 * 用来将文件上传记录保存到数据库,这里使用了 MyBatis-Plus 和 Hutool 工具类
 */
@Service
public class FileDetailService extends ServiceImpl<FileDetailMapper, FileDetail> implements FileRecorder {

    /**
     * 保存文件信息到数据库
     */
    @SneakyThrows
    @Override
    public boolean record(FileInfo info) {
        FileDetail detail = BeanUtil.copyProperties(info,FileDetail.class,"attr");

        //这是手动获 取附加属性字典 并转成 json 字符串,方便存储在数据库中
        if (info.getAttr() != null) {
            detail.setAttr(new ObjectMapper().writeValueAsString(info.getAttr()));
        }
        boolean b = save(detail);
        if (b) {
            info.setId(detail.getId());
        }
        return b;
    }

    /**
     * 根据 url 查询文件信息
     */
    @SneakyThrows
    @Override
    public FileInfo getByUrl(String url) {
        FileDetail detail = getOne(new QueryWrapper<FileDetail>().eq(FileDetail.COL_URL,url));
        FileInfo info = BeanUtil.copyProperties(detail,FileInfo.class,"attr");

        //这是手动获取数据库中的 json 字符串 并转成 附加属性字典,方便使用
        if (StrUtil.isNotBlank(detail.getAttr())) {
            info.setAttr(new ObjectMapper().readValue(detail.getAttr(),Dict.class));
        }
        return info;
    }

    /**
     * 根据 url 删除文件信息
     */
    @Override
    public boolean delete(String url) {
        return remove(new QueryWrapper<FileDetail>().eq(FileDetail.COL_URL,url));
    }
}

数据库表结构推荐如下,你也可以根据自己喜好在这里自己扩展

-- 这里使用的是 mysql
CREATE TABLE `file_detail`
(
    `id`                varchar(32)  NOT NULL COMMENT '文件id',
    `url`               varchar(512) NOT NULL COMMENT '文件访问地址',
    `size`              bigint(20)   DEFAULT NULL COMMENT '文件大小,单位字节',
    `filename`          varchar(256) DEFAULT NULL COMMENT '文件名称',
    `original_filename` varchar(256) DEFAULT NULL COMMENT '原始文件名',
    `base_path`         varchar(256) DEFAULT NULL COMMENT '基础存储路径',
    `path`              varchar(256) DEFAULT NULL COMMENT '存储路径',
    `ext`               varchar(32)  DEFAULT NULL COMMENT '文件扩展名',
    `content_type`      varchar(32)  DEFAULT NULL COMMENT 'MIME类型',
    `platform`          varchar(32)  DEFAULT NULL COMMENT '存储平台',
    `th_url`            varchar(512) DEFAULT NULL COMMENT '缩略图访问路径',
    `th_filename`       varchar(256) DEFAULT NULL COMMENT '缩略图名称',
    `th_size`           bigint(20)   DEFAULT NULL COMMENT '缩略图大小,单位字节',
    `th_content_type`   varchar(32)  DEFAULT NULL COMMENT '缩略图MIME类型',
    `object_id`         varchar(32)  DEFAULT NULL COMMENT '文件所属对象id',
    `object_type`       varchar(32)  DEFAULT NULL COMMENT '文件所属对象类型,例如用户头像,评价图片',
    `attr`              text COMMENT '附加属性',
    `create_time`       datetime     DEFAULT NULL COMMENT '创建时间',
    PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8
  ROW_FORMAT = DYNAMIC COMMENT ='文件记录表';

下载

多种下载方式

// 获取文件信息
FileInfo fileInfo = fileStorageService.getFileInfoByUrl("http://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("http://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("http://file.abc.com/test/a.jpg");

//直接删除
fileStorageService.delete(fileInfo);

//条件删除
fileStorageService.delete(fileInfo,info -> {
    //TODO 检查是否满足删除条件
    return true;
});

//直接通过文件信息中的 url 删除,省去手动查询文件信息记录的过程
fileStorageService.delete("http://file.abc.com/test/a.jpg");

判断文件是否存在

//获取文件信息
FileInfo fileInfo = fileStorageService.getFileInfoByUrl("http://file.abc.com/test/a.jpg");

//判断文件是否存在
boolean exists = fileStorageService.exists(fileInfo);

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

存储平台

目前支持多种存储平台,你也可以根据需要自行扩展

支持的存储平台

平台

支持

本地

FTP

SFTP

WebDAV

平台

官方 SDK

AWS S3 SDK

S3 兼容说明

AWS S3

-

MinIO

查看

阿里云 OSS

查看

华为云 OBS

查看

七牛云 Kodo

查看

腾讯云 COS

查看

百度云 BOS

查看

又拍云 USS

×

-

金山云 KS3

×

查看

美团云 MSS

×

查看

京东云 OSS

×

查看

天翼云 OOS

×

查看

移动云 EOS

×

查看

沃云 OSS

×

查看

网易数帆 NOS

×

查看

Ucloud US3

×

查看

青云 QingStor

×

查看

平安云 OBS

×

查看

首云 OSS

×

查看

IBM COS

×

查看

谷歌云存储

×

-

其它兼容 S3 协议的平台

×

-

自定义存储平台

想要自定义存储平台就要实现 FileStorage 这个接口,并进行实例化,注意返回的 bean 是个 list

这里拿 LocalFileStorage 举例


/**
 * 实现 FileStorage 接口,这里使用了 Lombok 和 Hutool 工具类
 */
@Getter
@Setter
public class LocalFileStorage implements FileStorage {

    /* 本地存储路径*/
    private String basePath;
    /* 存储平台 */
    private String platform;
    /* 访问域名 */
    private String domain;

    /**
     * 释放资源
     */
    @Override
    public void close() {
    }

    /**
     * 保存文件
     */
    @Override
    public boolean save(FileInfo fileInfo,UploadPretreatment pre) {
        String path = fileInfo.getPath();

        File newFile = FileUtil.touch(basePath + path,fileInfo.getFilename());
        fileInfo.setBasePath(basePath);
        fileInfo.setUrl(domain + path + fileInfo.getFilename());

        try {
            pre.getFileWrapper().transferTo(newFile);

            byte[] thumbnailBytes = pre.getThumbnailBytes();
            if (thumbnailBytes != null) { //上传缩略图
                fileInfo.setThUrl(fileInfo.getUrl() + pre.getThumbnailSuffix());
                FileUtil.writeBytes(thumbnailBytes,newFile.getPath() + pre.getThumbnailSuffix());
            }
            return true;
        } catch (IOException e) {
            FileUtil.del(newFile);
            throw new FileStorageRuntimeException("文件上传失败!platform:" + platform + ",filename:" + fileInfo.getOriginalFilename(),e);
        }
    }

    /**
     * 删除文件
     */
    @Override
    public boolean delete(FileInfo fileInfo) {
        if (fileInfo.getThFilename() != null) {   //删除缩略图
            FileUtil.del(new File(fileInfo.getBasePath() + fileInfo.getPath(),fileInfo.getThFilename()));
        }
        return FileUtil.del(new File(fileInfo.getBasePath() + fileInfo.getPath(),fileInfo.getFilename()));
    }

    /**
     * 文件是否存在
     */
    @Override
    public boolean exists(FileInfo fileInfo) {
        return new File(fileInfo.getBasePath() + fileInfo.getPath(),fileInfo.getFilename()).exists();
    }

    /**
     * 下载文件
     */
    @Override
    public void download(FileInfo fileInfo,Consumer<InputStream> consumer) {
        try (InputStream in = FileUtil.getInputStream(fileInfo.getBasePath() + fileInfo.getPath() + fileInfo.getFilename())) {
            consumer.accept(in);
        } catch (IOException e) {
            throw new FileStorageRuntimeException("文件下载失败!platform:" + fileInfo,e);
        }
    }

    /**
     * 下载缩略图文件
     */
    @Override
    public void downloadTh(FileInfo fileInfo,Consumer<InputStream> consumer) {
        if (StrUtil.isBlank(fileInfo.getThFilename())) {
            throw new FileStorageRuntimeException("缩略图文件下载失败,文件不存在!fileInfo:" + fileInfo);
        }
        try (InputStream in = FileUtil.getInputStream(fileInfo.getBasePath() + fileInfo.getPath() + fileInfo.getThFilename())) {
            consumer.accept(in);
        } catch (IOException e) {
            throw new FileStorageRuntimeException("缩略图文件下载失败!fileInfo:" + fileInfo,e);
        }
    }
}

/**
 * 初始化
 */
@Configuration
public class LocalFileStorageAutoConfiguration {

    /**
     * 这里拿本地存储做个演示,注意返回的是个List
     */
    @Bean
    public List<LocalFileStorage> localFileStorageList() {
        ArrayList<LocalFileStorage> list = new ArrayList<>();
        LocalFileStorage localFileStorage = new LocalFileStorage();
        localFileStorage.setPlatform("my-local-1");//平台名称
        localFileStorage.setBasePath("");
        localFileStorage.setDomain("");
        list.add(localFileStorage);
        return list;
    }
}

动态增减存储平台

//获得存储平台 List
CopyOnWriteArrayList<FileStorage> list = fileStorageService.getFileStorageList();

//增加
LocalFileStorage storage = new LocalFileStorage();
storage.setPlatform("my-local-1");//平台名称
storage.setBasePath("");
storage.setDomain("");
list.add(storage);

//删除
FileStorage myLocal = fileStorageService.getFileStorage("my-local-1");
list.remove(myLocal);
myLocal.close();//释放资源

获取对应存储平台

AliyunOssFileStorage storage = (AliyunOssFileStorage) fileStorageService.getFileStorage("aliyun-oss-1");
OSS client = storage.getClient();
//获取到对应SDK的原生客户端对象,可以进行更多高级操作

切面

通过切面,可以对文件的上传和删除等进行干预

自定义切面

只需要实现FileStorageAspect接口

不需要的方法可以不用实现,此接口里的方法全部都有默认实现

/**
 * 使用切面打印文件上传和删除的日志
 */
@Slf4j
@Component
public class LogFileStorageAspect implements FileStorageAspect {

    /**
     * 上传,成功返回文件信息,失败返回 null
     */
    @Override
    public FileInfo uploadAround(UploadAspectChain chain,FileInfo fileInfo,UploadPretreatment pre,FileStorage fileStorage,FileRecorder fileRecorder) {
        log.info("上传文件 before -> {}",fileInfo);
        fileInfo = chain.next(fileInfo,pre,fileStorage,fileRecorder);
        log.info("上传文件 after -> {}",fileInfo);
        return fileInfo;
    }

    /**
     * 删除文件,成功返回 true
     */
    @Override
    public boolean deleteAround(DeleteAspectChain chain,FileInfo fileInfo,FileStorage fileStorage,FileRecorder fileRecorder) {
        log.info("删除文件 before -> {}",fileInfo);
        boolean res = chain.next(fileInfo,fileStorage,fileRecorder);
        log.info("删除文件 after -> {}",res);
        return res;
    }

    /**
     * 文件是否存在
     */
    @Override
    public boolean existsAround(ExistsAspectChain chain,FileInfo fileInfo,FileStorage fileStorage) {
        log.info("文件是否存在 before -> {}",fileInfo);
        boolean res = chain.next(fileInfo,fileStorage);
        log.info("文件是否存在 after -> {}",res);
        return res;
    }

    /**
     * 下载文件
     */
    @Override
    public void downloadAround(DownloadAspectChain chain,FileInfo fileInfo,FileStorage fileStorage,Consumer<InputStream> consumer) {
        log.info("下载文件 before -> {}",fileInfo);
        chain.next(fileInfo,fileStorage,consumer);
        log.info("下载文件 after -> {}",fileInfo);
    }

    /**
     * 下载缩略图文件
     */
    @Override
    public void downloadThAround(DownloadThAspectChain chain,FileInfo fileInfo,FileStorage fileStorage,Consumer<InputStream> consumer) {
        log.info("下载缩略图文件 before -> {}",fileInfo);
        chain.next(fileInfo,fileStorage,consumer);
        log.info("下载缩略图文件 after -> {}",fileInfo);
    }
}

动态增减切面

//获得切面 List
CopyOnWriteArrayList<FileStorageAspect> list = fileStorageService.getAspectList();

//增加
FileStorageAspect aspect = new LogFileStorageAspect();
list.add(aspect);

//删除
list.remove(aspect);

//条件删除
list.removeIf(item -> item instanceof LogFileStorageAspect);

常见问题

为什么我在application.yml 中已经添加对应的存储平台配置,上传时却提示我“没有找到对应的存储平台!”

检查pom.xml,是否引入对应平台的依赖,例如阿里云

<!-- 阿里云 OSS 不使用的情况下可以不引入 -->
<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.6.0</version>
</dependency>

如何根据数据库配置加载存储平台?

参考 动态增减存储平台

如何自定义缩略图后缀?

参考 多种上传方式 中的缩略图上传方式,可以自己调整缩略图后缀

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Cloud Stream 是 Spring Cloud 生态系统中的一部分,它提供了一种简单且可扩展的方式来构建消息驱动的微服务应用程序。而 RocketMQ 是一款开源的分布式消息中间件,它具有高可靠、高吞吐量、高可扩展性等特点。在 Spring Cloud Stream 中,我们可以通过集成 RocketMQ 来实现消息驱动的微服务应用程序。 下面是 Spring Cloud Stream 集成 RocketMQ 的详细文档: 1. 添加依赖 首先,我们需要添加 Spring Cloud Stream 和 RocketMQ 的相关依赖。在 pom.xml 文件中添加以下依赖: ``` <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-stream</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-stream-binder-rocketmq</artifactId> </dependency> <dependency> <groupId>org.apache.rocketmq</groupId> <artifactId>rocketmq-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> ``` 2. 配置 RocketMQ 在 application.properties 文件中添加 RocketMQ 的相关配置,例如: ``` spring.cloud.stream.rocketmq.binder.namesrv-addr=127.0.0.1:9876 spring.cloud.stream.rocketmq.binder.group=rocketmq-group rocketmq.name-server=127.0.0.1:9876 rocketmq.producer.group=rocketmq-producer-group rocketmq.consumer.group=rocketmq-consumer-group ``` 3. 定义消息通道 在 Spring Cloud Stream 中,消息是通过消息通道来传递的。我们需要定义输入通道和输出通道,例如: ``` public interface MyChannel { String INPUT = "my_input"; String OUTPUT = "my_output"; @Input(INPUT) SubscribableChannel input(); @Output(OUTPUT) MessageChannel output(); } ``` 4. 发送消息 我们可以通过注入 MessageChannel 来发送消息,例如: ``` @Autowired @Qualifier(MyChannel.OUTPUT) private MessageChannel myOutput; public void sendMessage(String message) { myOutput.send(MessageBuilder.withPayload(message).build()); } ``` 5. 接收消息 我们可以通过注入 SubscribableChannel 来接收消息,例如: ``` @StreamListener(MyChannel.INPUT) public void handleMessage(Message<String> message) { log.info("Received message: {}", message.getPayload()); } ``` 6. 集成 RocketMQ 消费者 我们也可以通过集成 RocketMQ 的消费者来接收消息,例如: ``` @Slf4j @Component @RocketMQMessageListener(consumerGroup = "${rocketmq.consumer.group}", topic = MyChannel.INPUT) public class MyRocketMQConsumer implements RocketMQListener<String> { @Override public void onMessage(String message) { log.info("Received message: {}", message); } } ``` 7. 集成 RocketMQ 生产者 我们也可以通过集成 RocketMQ 的生产者来发送消息,例如: ``` @Slf4j @Component public class MyRocketMQProducer { @Autowired private RocketMQTemplate rocketMQTemplate; public void sendMessage(String message) { rocketMQTemplate.convertAndSend(MyChannel.OUTPUT, message); } } ``` 以上就是 Spring Cloud Stream 集成 RocketMQ 的详细文档。通过这种方式,我们可以快速构建消息驱动的微服务应用程序,并且具有高可靠、高吞吐量、高可扩展性等特点。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值