前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >SpringBoot - FileStorage Starter场景启动器

SpringBoot - FileStorage Starter场景启动器

作者头像
小小工匠
发布于 2022-04-21 00:11:27
发布于 2022-04-21 00:11:27
1.2K00
代码可运行
举报
文章被收录于专栏:小工匠聊架构小工匠聊架构
运行总次数:0
代码可运行

文章目录

Pre

Spring Boot - 手把手教小师妹自定义Spring Boot Starter

需求

系统中,文件存储是个非常常规的需求,大家都需要重复开发,何不封装一个starter支持多协议文件存储的呢?

目前规划了如下的功能:

  1. 支持 多种存储, FTP , SFTP ,本地存储 , S3协议客户端(MINIO、 阿里云等)
  2. 支持自定义属性配置
  3. 开箱即用

使用步骤

各位看官,先看看符不符合你的需要,先演示下开发完成后的如何集成到自己的业务系统中。

1. 引入pom依赖

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	 <dependency>
            <groupId>com.artisangroupId>
            <artifactId>artisan-filestorage-spring-boot-starterartifactId>
            <version>1.0version>
        dependency>

2. 配置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
artisan:
  filestorage:
    storage-type: s3
    ftp:
      host: 192.168.126.140
      port: 21
      username: ftptest
      password: ftptest
      mode: Passive
      base-path: /artisan
    s3:
      endpoint: http://192.168.126.140:9000
      access-key: admin
      access-secret: password
      bucket: artisan-bucket
    sftp:
      base-path: /root/abc
      username: root
      password: artisan
      host: 192.168.126.140
      port: 22
    local:
      base-path: D://test

核心: 根据 storage-type 来决定实例化哪种实例对象。 其它配置为实例对象的属性配置。

2. 使用注解

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.artisan.doc.controller;

import cn.hutool.core.io.IoUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import net.zfsy.frame.file.storage.FileStorageFactory;
import net.zfsy.frame.operatelog.core.util.ServletUtils;
import org.springframework.http.HttpStatus;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */

@Api(tags = "S3文件存储")
@RestController
@RequestMapping("/s3")
@Validated
@Slf4j
public class S3FileController {

    @Resource
    private FileStorageFactory fileStorageFactory;

    @PostMapping("/upload")
    @ApiOperation("上传文件")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "path", value = "文件相对路径", example = "soft", dataTypeClass = String.class),
            @ApiImplicitParam(name = "file", value = "文件附件", required = true, dataTypeClass = MultipartFile.class)
    })
    public String uploadFile(String path, @RequestParam("file") MultipartFile file) throws Exception {

        return fileStorageFactory.getStorage().createFile(path, file.getOriginalFilename(), IoUtil.readBytes(file.getInputStream()));
    }

    @DeleteMapping("/delete")
    @ApiOperation("删除文件")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "path", value = "文件相对路径", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(name = "fileName", value = "文件名称", required = true, dataTypeClass = String.class)
    })
    public void deleteFile(String path, @RequestParam("fileName") String fileName) throws Exception {
        fileStorageFactory.getStorage().deleteFile(path, fileName);
    }

    @GetMapping("/get")
    @ApiOperation("下载文件")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "path", value = "文件相对路径", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(name = "fileName", value = "文件名称", required = true, dataTypeClass = String.class)
    })
    public void getFileContent(HttpServletResponse response,
                               String path, @RequestParam("fileName") String fileName) throws Exception {
        byte[] content = fileStorageFactory.getStorage().getFileContent(path, fileName);
        if (content == null) {
            log.warn("[getFileContent][path({}) fileName({}) 文件不存在]", path, fileName);
            response.setStatus(HttpStatus.NOT_FOUND.value());
            return;
        }
        ServletUtils.writeAttachment(response, fileName, content);
    }


}

实现

自动装配类 和 属性文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * @author 小工匠
 * @version 1.0
 * @date 2022/4/16 19:12
 * @mark: show me the code , change the world
 */
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ArtisanFileUploadProperties.class)
@ConditionalOnProperty(prefix = ArtisanFileUploadProperties.PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true)
public class ArtisanFileUploadAutoConfiguration implements InitializingBean, DisposableBean {

    private static final Logger logger = LoggerFactory.getLogger(ZhongFuFileUploadAutoConfiguration.class);

    private ZhongFuFileUploadProperties config;

    public ZhongFuFileUploadAutoConfiguration(ZhongFuFileUploadProperties config) {
        this.config = config;
    }

    /**
     *
     * @return 文件存储工厂对象
     */
    @Bean
    public FileStorageFactory fileStorageFactory(){
        return new FileStorageFactory(config);
    }

    @Override
    public void destroy() {
        logger.info("<== 【销毁--自动化配置】----多协议文件上传组件【ZhongFuFileUploadAutoConfiguration】");
    }

    @Override
    public void afterPropertiesSet() {
        logger.info("==> 【初始化--自动化配置】----多协议文件上传组件【ZhongFuFileUploadAutoConfiguration】");
    }
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package net.zfsy.frame.file.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

/**
 * @author 小工匠
 * @version 1.0
 * @date 2022/4/18 22:55
 * @mark: show me the code , change the world
 */
@ConfigurationProperties(ArtisanFileUploadProperties.PREFIX)
@Data
public class ArtisanFileUploadProperties {

    /**
     * 属性配置前缀
     */
    public static final String PREFIX = "zf.filestorage";

    /**
     * 文件服务类型
     * 1. file:本地磁盘
     * 2. ftp:FTP 服务器
     * 3. sftp:SFTP 服务器
     * 4. s3:支持 S3 协议的云存储服务,比如 MinIO、阿里云、华为云、腾讯云、七牛云等等
     */
    private StorageType storageType;

    private LocalStorageProperties local;

    private FtpStorageProperties ftp;

    private SftpStorageProperties sftp;

    private S3StorageProperties s3;


    /**
     * Type of Storage  to use.
     */
    public enum StorageType {

        /**
         * 本地存储
         */
        local,

        /**
         * ftp存储
         */
        ftp,

        /**
         * sftp存储
         */
        sftp,

        /**
         * s3协议的存储,比如minio
         */
        s3
    }

    /**
     * Local
     */
    @Data
    public static class LocalStorageProperties {

        /**
         * 基础路径
         */
        @NotEmpty(message = "基础路径不能为空")
        private String basePath;

    }


    /**
     * FTP
     */
    @Data
    public static class FtpStorageProperties {

        /**
         * 基础路径
         * 
         * 1. basePath为null或""上传到当前路径
         * 2. basePath为相对路径则相对于当前路径的子路径
         * 3. basePath为绝对路径则上传到此路径
         */
        @NotEmpty(message = "基础路径不能为空")
        private String basePath;

        /**
         * 主机地址
         */
        @NotEmpty(message = "host 不能为空")
        private String host;

        /**
         * 主机端口
         */
        @NotNull(message = "port 不能为空")
        private Integer port;

        /**
         * 用户名
         */
        @NotEmpty(message = "用户名不能为空")
        private String username;

        /**
         * 密码
         */
        @NotEmpty(message = "密码不能为空")
        private String password;

        /**
         * 连接模式
         * 
         * 使用 {@link  cn.hutool.extra.ftp.FtpMode} 对应的字符串
         * Active 主动模式
         * Passive 被动模式 (推荐)
         */
        @NotEmpty(message = "连接模式不能为空")
        private String mode;
    }

    @Data
    public static class SftpStorageProperties {

        /**
         * 基础路径
         */
        @NotEmpty(message = "基础路径不能为空")
        private String basePath;


        /**
         * 主机地址
         */
        @NotEmpty(message = "host 不能为空")
        private String host;

        /**
         * 主机端口
         */
        @NotNull(message = "port 不能为空")
        private Integer port;

        /**
         * 用户名
         */
        @NotEmpty(message = "用户名不能为空")
        private String username;

        /**
         * 密码
         */
        @NotEmpty(message = "密码不能为空")
        private String password;

    }

    /**
     * S3协议
     */
    @Data
    public static class S3StorageProperties {

        /**
         * 节点地址 MinIO:http://127.0.0.1:9000
         */
        @NotNull(message = "endpoint 不能为空")
        private String endpoint;


        /**
         * 存储 Bucket
         */
        @NotNull(message = "bucket 不能为空")
        private String bucket;

        /**
         * 访问 Key
         */
        @NotNull(message = "accessKey 不能为空")
        private String accessKey;
        /**
         * 访问 Secret
         */
        @NotNull(message = "accessSecret 不能为空")
        private String accessSecret;

    }
}

FileStorageFactory

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * @author 小工匠
 * @version 1.0
 * @date 2022/4/19 18:40
 * @mark: show me the code , change the world
 */

public class FileStorageFactory {

    private Logger logger = LoggerFactory.getLogger(FileStorageFactory.class);
    /**
     * 存储配置信息
     */
    private ZhongFuFileUploadProperties config;

    /**
     * 文件存储类型 和 实例化存储对象 映射关系
     */
    private Map<String, FileStorage> uploader = new ConcurrentHashMap<>();

    /**
     * 构造函数
     *
     * @param config
     */
    public FileStorageFactory(ZhongFuFileUploadProperties config) {
        this.config = config;
    }

    /**
     * @return 文件存储对象
     */
    public FileStorage getStorage() {
        // 获取配置文件中配置的存储类型
        String type = config.getStorageType().name();
        // 缓存对象,避免重复创建
        if (ZhongFuFileUploadProperties.StorageType.local.name().equalsIgnoreCase(type) && uploader.get(type) == null) {
            uploader.put(type, new LocalFileStorage(config));
        } else if (ZhongFuFileUploadProperties.StorageType.ftp.name().equalsIgnoreCase(type) && uploader.get(type) == null) {
            uploader.put(type, new FtpFileStorage(config));
        } else if (ZhongFuFileUploadProperties.StorageType.sftp.name().equalsIgnoreCase(type) && uploader.get(type) == null) {
            uploader.put(type, new SftpFileStorage(config));
        } else if (ZhongFuFileUploadProperties.StorageType.s3.name().equalsIgnoreCase(type) && uploader.get(type) == null) {
            uploader.put(type, new S3FileStorage(config));
        } else {
            if (uploader.get(type) == null) {
                uploader.put(type, new LocalFileStorage(config));
                logger.warn("未找到配置的文件存储类型, 将使用默认LocalFileStorage");
            }
        }
        // 返回实例化存储对象
        return uploader.get(type);
    }

}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 文件 Storage  接口
 * @author artisan
 */
public interface FileStorage {


    /**
     * 保存文件
     *
     * @param path    文件路径
     * @param path    文件名称
     * @param content 文件内容
     * @return 文件路径
     */
    String createFile(String path, String fileName, byte[] content) throws Exception;

    /**
     * 删除文件
     *
     * @param path 相对路径
     * @throws Exception 删除文件时,抛出 Exception 异常
     */
    void deleteFile(String path, String fileName) throws Exception;

    /**
     * 获得文件内容
     *
     * @param path 文件路径
     * @param fileName 文件名
     * @return 文件内容
     */
    byte[] getFileContent(String path, String fileName) throws Exception;

}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * @author 小工匠
 * @version 1.0
 * @date 2022/4/19 10:42
 * @mark: show me the code , change the world
 */
public abstract class AbstractFileStorage implements FileStorage {

    /**
     * 业务扩展
     */
    public final void ext() {
        doExt();
    }

    /**
     * 自定义业务扩展
     */
    protected abstract void doExt();

本地存储实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 本地文件 Storage 实现类
 *
 * @author artisan
 */
public class LocalFileStorage extends AbstractFileStorage {

    private Logger logger = LoggerFactory.getLogger(LocalFileStorage.class);

    private ZhongFuFileUploadProperties config;

    public LocalFileStorage(ZhongFuFileUploadProperties config) {
        this.config = config;
        ZhongFuFileUploadProperties.LocalStorageProperties local = this.config.getLocal();
        Assert.notNull(local, "本地存储配置信息不能为空,请配置 basePath 属性");

        // 补全风格  Linux 是 /,Windows 是 \
        if (!local.getBasePath().endsWith(File.separator)) {
            local.setBasePath(local.getBasePath() + File.separator);
        }
        logger.info("初次调用, 实例化LocalFileStorage");
    }


    @Override
    public String createFile(String path, String fileName, byte[] content) {
        // 执行写入
        File file = FileUtil.writeBytes(content, getAbsFilePath(path, fileName));
        logger.info("LOCAL-文件写入操作:入参path->{} , 文件名->{} , 文件存储路径->{}", path, fileName, file.getAbsolutePath());
        return file.getAbsolutePath();
    }


    @Override
    public void deleteFile(String path, String fileName) {
        String filePath = getAbsFilePath(path, fileName);
        FileUtil.del(filePath);
        logger.info("LOCAL-文件删除操作:入参path->{} , 绝对路径->{}", path, filePath);
    }


    @Override
    public byte[] getFileContent(String path, String fileName) {
        String filePath = getAbsFilePath(path, fileName);
        logger.info("LOCAL-文件读取操作:入参path->{} , 绝对路径->{}", path, filePath);
        return FileUtil.readBytes(filePath);
    }

    /**
     * @param path     相对路径
     * @param fileName 文件名名称
     * @return 文件绝对路径
     */
    private String getAbsFilePath(String path, String fileName) {
        return StrUtil.isBlank(path) ? (config.getLocal().getBasePath() + File.separator + fileName) : (config.getLocal().getBasePath() + path + File.separator + fileName);
    }

    @Override
    protected void doExt() {

    }

FTP存储实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * Ftp Storage 实现类
 *
 * @author artisan
 */
public class FtpFileStorage extends AbstractFileStorage {

    private Logger logger = LoggerFactory.getLogger(FtpFileStorage.class);

    private Ftp ftp;

    private ZhongFuFileUploadProperties config;

    public FtpFileStorage(ZhongFuFileUploadProperties config) {
        this.config = config;

        ZhongFuFileUploadProperties.FtpStorageProperties ftpConfig = config.getFtp();
        Assert.notNull(ftpConfig, "ftp客户端配置信息不能为空");

        // TODO  fix me when publish  (File.separator )测试 临时使用 "/"
        if (!ftpConfig.getBasePath().endsWith("/")) {
            ftpConfig.setBasePath(ftpConfig.getBasePath() + "/");
        }

        // 初始化 Ftp 对象
        this.ftp = new Ftp(ftpConfig.getHost(), ftpConfig.getPort(), ftpConfig.getUsername(), ftpConfig.getPassword(),
                CharsetUtil.CHARSET_UTF_8, null, null, FtpMode.valueOf(ftpConfig.getMode()));

        logger.info("初次调用, 实例化FtpFileStorage");
    }

    @Override
    protected void doExt() {

    }

    /**
     * @param path     文件相对路径
     * @param fileName 文件名称
     * @param content  文件内容
     * @return 文件存储路径
     */
    @Override
    public String createFile(String path, String fileName, byte[] content) throws Exception {
        // 执行写入
        String destPath = config.getFtp().getBasePath() + path.trim();
        boolean success = ftp.upload(destPath, fileName, new ByteArrayInputStream(content));
        if (!success) {
            throw new FtpException(StrUtil.format("上传文件到目标目录 ({}) 失败", path));
        }
        logger.info("FTP-文件写入操作:入参path->{} , 文件名->{}  ", path, fileName);
        return path + File.separator + fileName;
    }

    @Override
    public byte[] getFileContent(String path, String fileName) {
        String filePath = config.getFtp().getBasePath() + path.trim();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ftp.download(filePath, fileName, out);
        logger.info("FTP-文件读取操作:入参path->{} , 绝对路径->{}", path, filePath);
        return out.toByteArray();
    }

    @Override
    public void deleteFile(String path, String fileName) throws Exception {
        // TODO  fix me when publish   测试 临时使用 "/"
        String filePath = config.getFtp().getBasePath() + path.trim() + "/" + fileName;
        boolean success = ftp.delFile(filePath);
        if (!success) {
            throw new FtpException(StrUtil.format("删除文件 ({}) 失败", filePath));
        }
        logger.info("FTP-文件删除操作:入参path->{} , 绝对路径->{}", path, filePath);
    }

SFTP存储实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * @author 小工匠
 * @version 1.0
 * @date 2022/4/20 10:21
 * @mark: show me the code , change the world
 */
public class SftpFileStorage extends AbstractFileStorage {

    private Logger logger = LoggerFactory.getLogger(SftpFileStorage.class);

    private Sftp sftp;

    private ZhongFuFileUploadProperties config;

    public SftpFileStorage(ZhongFuFileUploadProperties config) {
        this.config = config;
        ZhongFuFileUploadProperties.SftpStorageProperties sftpStorageProperties = this.config.getSftp();
        Assert.notNull(sftpStorageProperties, "Sftp客户端不能为空");

        // 补全风格。例如说 Linux 是 /,Windows 是 \   TODO
        if (!sftpStorageProperties.getBasePath().endsWith(File.separator)) {
            sftpStorageProperties.setBasePath(sftpStorageProperties.getBasePath() + "/");
        }
        // 初始化 Ftp 对象
        this.sftp = new Sftp(sftpStorageProperties.getHost(), sftpStorageProperties.getPort(), sftpStorageProperties.getUsername(), sftpStorageProperties.getPassword());
        // 创建目录
        sftp.mkdir(sftpStorageProperties.getBasePath());

        logger.info("初次调用, 实例化SftpFileStorage");
    }


    @Override
    protected void doExt() {

    }

    @Override
    public String createFile(String path, String fileName, byte[] content) throws Exception {
        // 创建目录
        sftp.mkdir(getDir(path));
        // 获取文件存储路径  TODO
        String destPath = getDestPath(path, fileName);
        // 根据文件名,创建文件
        File file = createFileByFileName(content, fileName);
        // 执行写入
        boolean success = sftp.upload(destPath, file);

        if (!success) {
            throw new SftpException(500, StrUtil.format("SFTP上传文件到目标目录 ({}) 失败", destPath));
        }
        logger.info("SFTP-文件写入操作:入参path->{} , 文件名->{}  ", path, fileName);
        // 拼接返回路径
        return destPath;
    }


    @Override
    public void deleteFile(String path, String fileName) throws Exception {
        String destPath = getDestPath(path, fileName);
        sftp.delFile(destPath);
        logger.info("Sftp-文件删除操作:入参path->{} , 文件名->{} , 文件存储路径->{}", path, fileName, destPath);
    }

    @Override
    public byte[] getFileContent(String path, String fileName) throws Exception {
        String filePath = getDestPath(path,fileName);
        File destFile = new File(fileName);
        sftp.download(filePath, destFile);
        return FileUtil.readBytes(destFile);
    }

    /**
     * @param path
     * @param fileName
     * @return 文件路径
     */
    private String getDestPath(String path, String fileName) {
        String destPath = config.getSftp().getBasePath() + path.trim() + "/" + fileName;
        return destPath;
    }

    /**
     * @param path
     * @return 文件目录
     */
    private String getDir(String path) {
        return config.getSftp().getBasePath() + path.trim();
    }

    /**
     * 创建文件
     *
     * @param fileName 文件名
     * @return 文件
     */
    @SneakyThrows
    public static File createFileByFileName(byte[] data ,String fileName) {
        File file = new File(fileName);
        // 标记 JVM 退出时,自动删除
        file.deleteOnExit();
        // 写入内容
        FileUtil.writeBytes(data, file);
        return file;
    }
}

S3存储实现(MINIO)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * @author 小工匠
 * @version 1.0
 * @date 2022/4/20 8:44
 * @mark: show me the code , change the world
 */
public class S3FileStorage extends AbstractFileStorage {

    private Logger logger = LoggerFactory.getLogger(LocalFileStorage.class);

    private MinioClient client;

    private ZhongFuFileUploadProperties config;

    public S3FileStorage(ZhongFuFileUploadProperties config) {
        this.config = config;
        ZhongFuFileUploadProperties.S3StorageProperties s3StorageProperties = this.config.getS3();
        Assert.notNull(s3StorageProperties, "S3协议客户端不能为空");

        validate(Validation.buildDefaultValidatorFactory().getValidator(), s3StorageProperties);

        // 初始化客户端
        client = MinioClient.builder()
                // Endpoint URL
                .endpoint(buildEndpointURL(s3StorageProperties))
                // 认证密钥
                .credentials(s3StorageProperties.getAccessKey(), s3StorageProperties.getAccessSecret())
                .build();
        // 创建Bucket
        checkBucket(s3StorageProperties.getBucket());

        logger.info("初次调用, 实例化S3FileStorage");
    }


    /**
     * 检查Bucket是否存在,不存在 创建
     *
     * @param bucketName bucket 名称
     */
    private void checkBucket(String bucketName) {
        try {
            if (!client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
                MakeBucketArgs makeArgs = MakeBucketArgs.builder().bucket(bucketName).build();
                client.makeBucket(makeArgs);
                logger.info("bucket {} 不存在, 自动创建该bucket", bucketName);
            }
        } catch (Exception e) {
            logger.error(" 自动创建bucket {} 异常", bucketName, e.getMessage());
        }
    }

    /**
     * 基于 endpoint 构建调用云服务的 URL 地址
     *
     * @return URI 地址
     */
    private String buildEndpointURL(ZhongFuFileUploadProperties.S3StorageProperties s3StorageProperties) {
        return s3StorageProperties.getEndpoint();
    }


    @Override
    protected void doExt() {

    }

    @Override
    public String createFile(String path, String fileName, byte[] content) throws Exception {
        // TODO
        String filePath = path + "/" + fileName;

        // 执行上传
        client.putObject(PutObjectArgs.builder()
                // bucket 必须传递
                .bucket(config.getS3().getBucket())
                // 相对路径作为 key
                .object(filePath)
                // 文件内容
                .stream(new ByteArrayInputStream(content), content.length, -1)
                .build());

        String url = config.getS3().getEndpoint() + "/" + config.getS3().getBucket() + "/"  + filePath;
        logger.info("S3-文件写入操作:入参path->{} , 文件名->{} ,文件路径->{}", path, fileName, url);

        // 拼接返回路径
        return url;
    }

    @Override
    public void deleteFile(String path, String fileName) throws Exception {
        // TODO
        String filePath = path + "/" + fileName;
        client.removeObject(RemoveObjectArgs.builder()
                // bucket 必须传递
                .bucket(config.getS3().getBucket())
                // 相对路径作为 key
                .object(filePath)
                .build());
        logger.info("S3-文件删除操作:入参path->{} , 文件名->{} , 文件存储路径->{}", path, fileName);
    }

    @Override
    public byte[] getFileContent(String path, String fileName) throws Exception {
        // TODO
        String filePath = path + "/" + fileName;
        GetObjectResponse response = client.getObject(GetObjectArgs.builder()
                // bucket 必须传递
                .bucket(config.getS3().getBucket())
                // 相对路径作为 key
                .object(filePath)
                .build());
        return IoUtil.readBytes(response);
    }

    /**
     *
     * @param validator
     * @param object
     * @param groups
     */
    private void validate(Validator validator, Object object, Class<?>... groups) {
        Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
        if (CollUtil.isNotEmpty(constraintViolations)) {
            throw new ConstraintViolationException(constraintViolations);
        }
    }
}

spring.factories

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.artisan.frame.file.config.ArtisanFileUploadAutoConfiguration

pom

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependencies>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starterartifactId>
        dependency>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-validationartifactId>
        dependency>

        <dependency>
            <groupId>org.slf4jgroupId>
            <artifactId>slf4j-apiartifactId>
        dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.coregroupId>
            <artifactId>jackson-databindartifactId>
        dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.coregroupId>
            <artifactId>jackson-coreartifactId>
        dependency>

        
        <dependency>
            <groupId>commons-netgroupId>
            <artifactId>commons-netartifactId>
        dependency>
        
        <dependency>
            <groupId>com.jcraftgroupId>
            <artifactId>jschartifactId>
        dependency>

        
        <dependency>
            <groupId>io.miniogroupId>
            <artifactId>minioartifactId>
        dependency>

        <dependency>
            <groupId>cn.hutoolgroupId>
            <artifactId>hutool-allartifactId>
        dependency>

        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.coregroupId>
            <artifactId>jackson-annotationsartifactId>
        dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.coregroupId>
            <artifactId>jackson-databindartifactId>
        dependency>

        <dependency>
            <groupId>org.slf4jgroupId>
            <artifactId>slf4j-apiartifactId>
        dependency>

        <dependency>
            <groupId>org.junit.jupitergroupId>
            <artifactId>junit-jupiter-apiartifactId>
            <scope>testscope>
        dependency>

        <dependency>
            <groupId>org.mockitogroupId>
            <artifactId>mockito-allartifactId>
            <scope>testscope>
        dependency>

    dependencies>

别忘了 spring-boot-configuration-processor 哦

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-configuration-processorartifactId>
        dependency>
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022/04/20 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
SpringBoot 使用 FTP 操作文件
因为在 Linux 上,root 用户是不能登陆 FTP 的。如果你输入的是 root 用户,登陆会失败的。
默存
2023/03/09
3.3K1
SpringBoot 使用 FTP 操作文件
基于spring boot sftp文件上传
对sftp文件上传将行封装,实现连接的单例模式,完成线程安全的改进,sftp文件上传下载失败的重试。
张泽旭
2018/12/10
2.9K0
Spring Boot 实现 SFTP 文件上传下载
近期由于系统迁移到docker容器,采用Spring Boot 框架实现微服务治理,在此基础上晚间批量文件服务器也由ftp改成sftp,由于之前ftp的实现是采用公具类的形式,在此基础之上,未屏蔽开发细节和依赖Spring Boot自动装配的特性,进行组件的自动装配和改造,旨在实现简化开发,提高文件传输的安全性和数据交互的可靠性。
周三不加班
2019/06/04
5K0
性能工具之Jmeter通过springboot工程启动
Jmeter平时性能测试工作一般都是通过命令行在linux下执行,为了锻炼自己代码与逻辑能力,想jmeter是否可以通过springboot工程启动,周末在家尝试写一写,一写原来需要处理很多事情,才可以启动起来,起来还是有很问题需要处理,下面是相应的代码,其实网上也有,但关键的是自己有意识收集知识,到用的时候能拿来改一改就用。
高楼Zee
2019/11/25
2.1K0
性能工具之Jmeter通过springboot工程启动
springboot中使用SFTP文件上传
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
全栈程序员站长
2022/11/03
3.4K0
删除个文件夹,vfs2上传文件到ftp就异常553,这么不经事吗
基于 commons-vfs2 实现文件到 FTP 服务器的上传,pom.xml 如下
青石路
2024/08/14
1030
删除个文件夹,vfs2上传文件到ftp就异常553,这么不经事吗
基于spring boot ftp文件上传
对ftp文件上传将行封装,实现连接的单例模式,完成线程安全的改进,ftp文件上传下载失败的重试。
张泽旭
2018/12/10
7.2K0
某低代码平台代码审计分析
类似shiro的权限绕过,可以利用static/../je/document/file绕过
红队蓝军
2024/07/01
1140
某低代码平台代码审计分析
Sftp工具类
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import ja
一笠风雨任生平
2019/08/02
3.8K0
SpringBoot整合自定义FTP文件连接池
说明:通过GenericObjectPool实现的FTP连接池,记录一下以供以后使用 环境: JDK版本1.8 框架 :springboot2.1 文件服务器: Serv-U 1.引入依赖
庞小明
2019/05/25
3.4K0
【愚公系列】2022年03月 C#帮助类-FTP的花样操作
文件传输协议(File Transfer Protocol,FTP)是用于在网络上进行文件传输的一套标准协议,它工作在 OSI 模型的第七层, TCP 模型的第四层, 即应用层, 使用 TCP 传输而不是 UDP, 客户在和服务器建立连接前要经过一个“三次握手”的过程, 保证客户与服务器之间的连接是可靠的, 而且是面向连接, 为数据传输提供可靠保证。
愚公搬代码
2022/03/04
4480
FTP工具类
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.SocketException; import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPCli
一笠风雨任生平
2019/08/02
1.5K0
springboot(十八):使用Spring Boot集成FastDFS
上篇文章介绍了《如何使用Spring Boot上传文件》,这篇文章我们介绍如何使用Spring Boot将文件上传到分布式文件系统FastDFS中。 这个项目会在上一个项目的基础上进行构建。 1、pom包配置 我们使用Spring Boot最新版本1.5.9、jdk使用1.8、tomcat8.0。 <dependency> <groupId>org.csource</groupId> <artifactId>fastdfs-client-java</artifactId> <vers
纯洁的微笑
2018/04/18
1.4K0
springboot(十八):使用Spring Boot集成FastDFS
C# FTP上传、下载、删除
文件传输协议(File Transfer Protocol,FTP)是用于在网络上进行文件传输的一套标准协议,作为一套古老的网络工具,在工业界有着及其广泛的应用.本节主要给大家演示ftp对文件的上传、下载、以及删除。如果还没有ftp服务地址,请参考上节【使用filezilla server搭建ftp服务器】搭建下服务器。
用户9127601
2022/03/23
3.1K1
C# FTP上传、下载、删除
文件系统(02):基于SpringBoot框架,管理Xml和CSV
XML是可扩展标记语言,是一种用于标记电子文件使其具有结构性的标记语言。标记指计算机所能理解的信息符号,通过此种标记,计算机之间可以处理包含各种的信息比如数据结构,格式等。它可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。适合网络传输,提供统一的方法来描述和交换应用程序的结构化数据。
知了一笑
2020/02/17
8920
文件系统(02):基于SpringBoot框架,管理Xml和CSV
【FastFDS】SpringBoot整合FastDFS实战(附完整工程源码)
作者个人研发的在高并发场景下,提供的简单、稳定、可扩展的延迟消息队列框架,具有精准的定时任务和延迟队列处理功能。自开源半年多以来,已成功为十几家中小型企业提供了精准定时调度方案,经受住了生产环境的考验。为使更多童鞋受益,现给出开源框架地址:
冰河
2020/10/29
7240
【FastFDS】SpringBoot整合FastDFS实战(附完整工程源码)
Java通过FTP实现上传下载
今天是2022年9月27日,突发灵感再java中如何通过ftp实现上传以及下载,那么此文Java通过FTP实现上传下载为大家讲述一整套流程。
小颜同学
2023/08/24
8610
造轮子之文件管理
前面我们完成了设置管理,接下来正好配合设置管理来实现文件管理功能。 文件管理自然包括文件上传,下载以及文件存储功能。设计要求可以支持扩展多种存储服务,如本地文件,云存储等等。
饭勺oO
2023/10/25
3180
造轮子之文件管理
使用JSch库进行SFTP连接
JSch是一个Java库,用于实现SSH2协议。JSch可以用于连接SSH服务器并进行各种远程操作,如文件传输、端口转发、远程执行命令等。
品茗IT
2023/10/22
1.5K0
sftp使用方法_sftp服务
参考:https://www.jianshu.com/p/64d571913185
全栈程序员站长
2022/11/10
5.5K0
相关推荐
SpringBoot 使用 FTP 操作文件
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文