手把手教你SpringBoot集成SFTP客户端实现文件上传下载(下)

简介: 手把手教你SpringBoot集成SFTP客户端实现文件上传下载

为了实现真正的池化操作,我们还需要以下代码:

1.我们需要在SftpClient对象中创建一个GenericObjectPool对象池,这个才是真正的池子,它负责创建和存储所有的对象。

2.我们还需要提供资源销毁的功能,也就是实现AutoCloseable,在服务停止时,需要把相关的资源销毁。

public class SftpClient implements AutoCloseable {
  private SftpFactory sftpFactory;
  GenericObjectPool<ChannelSftp> objectPool;
  // 构造方法1
  public SftpClient(SftpProperties properties, GenericObjectPoolConfig<ChannelSftp> poolConfig) throws AwesomeException {
    this.sftpFactory = new SftpFactory(properties);
    objectPool = new GenericObjectPool<>(this.sftpFactory, poolConfig);
  }
  // 构造方法2
  public SftpClient(SftpProperties properties) throws AwesomeException {
    this.sftpFactory = new SftpFactory(properties);
    SftpProperties.PoolConfig config = properties.getPool();
    // 默认池化配置
    if (Objects.isNull(config)) {
      objectPool = new GenericObjectPool<>(this.sftpFactory);
    } else {
      // 自定义池化配置
      GenericObjectPoolConfig<ChannelSftp> poolConfig = new GenericObjectPoolConfig<>();
      poolConfig.setMaxIdle(config.getMaxIdle());
      poolConfig.setMaxTotal(config.getMaxTotal());
      poolConfig.setMinIdle(config.getMinIdle());
      poolConfig.setTestOnBorrow(config.isTestOnBorrow());
      poolConfig.setTestOnCreate(config.isTestOnCreate());
      poolConfig.setTestOnReturn(config.isTestOnReturn());
      poolConfig.setTestWhileIdle(config.isTestWhileIdle());
      poolConfig.setBlockWhenExhausted(config.isBlockWhenExhausted());
      poolConfig.setMaxWait(Duration.ofMillis(config.getMaxWaitMillis()));
      poolConfig.setTimeBetweenEvictionRuns(Duration.ofMillis(config.getTimeBetweenEvictionRunsMillis()));
      objectPool = new GenericObjectPool<>(this.sftpFactory, poolConfig);
    }
  }
  // 销毁资源
    @Override
  public void close() throws Exception {
    // 销毁链接池
    if (Objects.nonNull(this.objectPool)) {
      if (!this.objectPool.isClosed()) {
        this.objectPool.close();
      }
    }
    this.objectPool = null;
    // 销毁sftpFactory
    if (Objects.nonNull(this.sftpFactory)) {
      this.sftpFactory.close();
    }
  }
}
复制代码

SFTP链接池的使用

我们已经对链接池进行了初始化,下面我们就可以从链接池中获取我们需要的ChannelSftp来实现文件的上传下载了。

下面实现了多种文件上传和下载的方式:

1.直接把本地文件上传到SFTP服务器的指定路径;

2.把InputStream输入流提交到SFTP服务器指定路径中;

3.可以针对以上两种上传方式进行进度的监测;

4.把SFTP服务器中的指定文件下载到本地机器上;

5.把SFTP服务器˙中的文件写入指定的输出流;

6.针对以上两种下载方式,监测下载进度;

/**
   * 上传文件
   *
   * @param srcFilePath
   * @param targetDir
   * @param targetFileName
   * @return
   * @throws AwesomeException
   */
  public boolean uploadFile(String srcFilePath, String targetDir, String targetFileName) throws AwesomeException {
    return uploadFile(srcFilePath, targetDir, targetFileName, null);
  }
  /**
   * 上传文件
   *
   * @param srcFilePath
   * @param targetDir
   * @param targetFileName
   * @param monitor
   * @return
   * @throws AwesomeException
   */
  public boolean uploadFile(String srcFilePath, String targetDir, String targetFileName, SftpProgressMonitor monitor) throws AwesomeException {
    ChannelSftp channelSftp = null;
    try {
      // 从链接池获取对象
      channelSftp = this.objectPool.borrowObject();
      // 如果不存在目标文件夹
      if (!exist(channelSftp, targetDir)) {
        mkdirs(channelSftp, targetDir);
      }
      channelSftp.cd(targetDir);
      // 上传文件
      if (Objects.nonNull(monitor)) {
        channelSftp.put(srcFilePath, targetFileName, monitor);
      } else {
        channelSftp.put(srcFilePath, targetFileName);
      }
      return true;
    } catch (Exception e) {
      throw new AwesomeException(500, "upload file fail");
    } finally {
      if (Objects.nonNull(channelSftp)) {
        // 返还对象给链接池
        this.objectPool.returnObject(channelSftp);
      }
    }
  }
  /**
   * 上传文件到目标文件夹
   *
   * @param in
   * @param targetDir
   * @param targetFileName
   * @return
   * @throws AwesomeException
   */
  public boolean uploadFile(InputStream in, String targetDir, String targetFileName) throws AwesomeException {
    return uploadFile(in, targetDir, targetFileName, null);
  }
  /**
   * 上传文件,添加进度监视器
   *
   * @param in
   * @param targetDir
   * @param targetFileName
   * @param monitor
   * @return
   * @throws AwesomeException
   */
  public boolean uploadFile(InputStream in, String targetDir, String targetFileName, SftpProgressMonitor monitor) throws AwesomeException {
    ChannelSftp channelSftp = null;
    try {
      channelSftp = this.objectPool.borrowObject();
      // 如果不存在目标文件夹
      if (!exist(channelSftp, targetDir)) {
        mkdirs(channelSftp, targetDir);
      }
      channelSftp.cd(targetDir);
      if (Objects.nonNull(monitor)) {
        channelSftp.put(in, targetFileName, monitor);
      } else {
        channelSftp.put(in, targetFileName);
      }
      return true;
    } catch (Exception e) {
      throw new AwesomeException(500, "upload file fail");
    } finally {
      if (Objects.nonNull(channelSftp)) {
        this.objectPool.returnObject(channelSftp);
      }
    }
  }
  /**
   * 下载文件
   *
   * @param remoteFile
   * @param targetFilePath
   * @return
   * @throws AwesomeException
   */
  public boolean downloadFile(String remoteFile, String targetFilePath) throws AwesomeException {
    return downloadFile(remoteFile, targetFilePath, null);
  }
  /**
   * 下载目标文件到本地
   *
   * @param remoteFile
   * @param targetFilePath
   * @return
   * @throws AwesomeException
   */
  public boolean downloadFile(String remoteFile, String targetFilePath, SftpProgressMonitor monitor) throws AwesomeException {
    ChannelSftp channelSftp = null;
    try {
      channelSftp = this.objectPool.borrowObject();
      // 如果不存在目标文件夹
      if (!exist(channelSftp, remoteFile)) {
        // 不用下载了
        return false;
      }
      File targetFile = new File(targetFilePath);
      try (FileOutputStream outputStream = new FileOutputStream(targetFile)) {
        if (Objects.nonNull(monitor)) {
          channelSftp.get(remoteFile, outputStream, monitor);
        } else {
          channelSftp.get(remoteFile, outputStream);
        }
      }
      return true;
    } catch (Exception e) {
      throw new AwesomeException(500, "upload file fail");
    } finally {
      if (Objects.nonNull(channelSftp)) {
        this.objectPool.returnObject(channelSftp);
      }
    }
  }
  /**
   * 下载文件
   *
   * @param remoteFile
   * @param outputStream
   * @return
   * @throws AwesomeException
   */
  public boolean downloadFile(String remoteFile, OutputStream outputStream) throws AwesomeException {
    return downloadFile(remoteFile, outputStream, null);
  }
  /**
   * 下载文件
   *
   * @param remoteFile
   * @param outputStream
   * @param monitor
   * @return
   * @throws AwesomeException
   */
  public boolean downloadFile(String remoteFile, OutputStream outputStream, SftpProgressMonitor monitor) throws AwesomeException {
    ChannelSftp channelSftp = null;
    try {
      channelSftp = this.objectPool.borrowObject();
      // 如果不存在目标文件夹
      if (!exist(channelSftp, remoteFile)) {
        // 不用下载了
        return false;
      }
      if (Objects.nonNull(monitor)) {
        channelSftp.get(remoteFile, outputStream, monitor);
      } else {
        channelSftp.get(remoteFile, outputStream);
      }
      return true;
    } catch (Exception e) {
      throw new AwesomeException(500, "upload file fail");
    } finally {
      if (Objects.nonNull(channelSftp)) {
        this.objectPool.returnObject(channelSftp);
      }
    }
  }
  /**
   * 创建文件夹
   *
   * @param channelSftp
   * @param dir
   * @return
   */
  protected boolean mkdirs(ChannelSftp channelSftp, String dir) {
    try {
      String pwd = channelSftp.pwd();
      if (StringUtils.contains(pwd, dir)) {
        return true;
      }
      String relativePath = StringUtils.substringAfter(dir, pwd);
      String[] dirs = StringUtils.splitByWholeSeparatorPreserveAllTokens(relativePath, "/");
      for (String path : dirs) {
        if (StringUtils.isBlank(path)) {
          continue;
        }
        try {
          channelSftp.cd(path);
        } catch (SftpException e) {
          channelSftp.mkdir(path);
          channelSftp.cd(path);
        }
      }
      return true;
    } catch (Exception e) {
      return false;
    }
  }
  /**
   * 判断文件夹是否存在
   *
   * @param channelSftp
   * @param dir
   * @return
   */
  protected boolean exist(ChannelSftp channelSftp, String dir) {
    try {
      channelSftp.lstat(dir);
      return true;
    } catch (Exception e) {
      return false;
    }
  }
复制代码

集成到SpringBoot中

我们可以通过java config的方式,把我们已经实现好的SftpClient类实例化到Spring IOC容器中来管理,以便让开发人员在整个项目中通过@Autowired的方式就可以直接使用。

  • 配置
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
 * @author zouwei
 * @className SftpProperties
 * @date: 2022/8/19 下午12:12
 * @description:
 */
@Data
@Configuration
@ConfigurationProperties(prefix = "sftp.config")
public class SftpProperties {
  // 用户名
  private String username;
  // 密码
  private String password;
  // 主机名
  private String host;
  // 端口
  private int port;
  // 密钥
  private String privateKeyFile;
  // 口令
  private String passphrase;
  // 通道链接超时时间
  private int timeout;
  // 链接池配置
  private PoolConfig pool;
  @Data
  public static class PoolConfig {
    //最大空闲实例数,空闲超过此值将会被销毁淘汰
    private int maxIdle;
    // 最小空闲实例数,对象池将至少保留2个空闲对象
    private int minIdle;
    //最大对象数量,包含借出去的和空闲的
    private int maxTotal;
    //对象池满了,是否阻塞获取(false则借不到直接抛异常)
    private boolean blockWhenExhausted;
    // BlockWhenExhausted为true时生效,对象池满了阻塞获取超时,不设置则阻塞获取不超时,也可在borrowObject方法传递第二个参数指定本次的超时时间
    private long maxWaitMillis;
    // 创建对象后是否验证对象,调用objectFactory#validateObject
    private boolean testOnCreate;
    // 借用对象后是否验证对象 validateObject
    private boolean testOnBorrow;
    // 归还对象后是否验证对象 validateObject
    private boolean testOnReturn;
    // 定时检查期间是否验证对象 validateObject
    private boolean testWhileIdle;
    //定时检查淘汰多余的对象, 启用单独的线程处理
    private long timeBetweenEvictionRunsMillis;
    //jmx监控,和springboot自带的jmx冲突,可以选择关闭此配置或关闭springboot的jmx配置
    private boolean jmxEnabled;
  }
}
复制代码
  • java Bean注入
import com.example.awesomespring.exception.AwesomeException;
import com.example.awesomespring.sftp.SftpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * @author zouwei
 * @className SftpConfig
 * @date: 2022/8/19 下午12:12
 * @description:
 */
@Configuration
public class SftpConfig {
  @Autowired
  private SftpProperties properties;
  // 创建SftpClient对象
  @Bean(destroyMethod = "close")
  @ConditionalOnProperty(prefix = "sftp.config")
  public SftpClient sftpClient() throws AwesomeException {
    return new SftpClient(properties);
  }
}
复制代码
  • 通过以上代码,我们就可以在项目的任何地方直接使用SFTP客户端来上传和下载文件了。


相关文章
|
1月前
|
Java Maven Docker
gitlab-ci 集成 k3s 部署spring boot 应用
gitlab-ci 集成 k3s 部署spring boot 应用
|
22天前
|
前端开发 Java easyexcel
SpringBoot操作Excel实现单文件上传、多文件上传、下载、读取内容等功能
SpringBoot操作Excel实现单文件上传、多文件上传、下载、读取内容等功能
64 8
|
17天前
|
XML Java 数据库连接
SpringBoot集成Flowable:打造强大的工作流管理系统
在企业级应用开发中,工作流管理是一个核心组件,它能够帮助我们定义、执行和管理业务流程。Flowable是一个开源的工作流和业务流程管理(BPM)平台,它提供了强大的工作流引擎和建模工具。结合SpringBoot,我们可以快速构建一个高效、灵活的工作流管理系统。本文将探讨如何将Flowable集成到SpringBoot应用中,并展示其强大的功能。
63 1
|
27天前
|
JSON Java API
springboot集成ElasticSearch使用completion实现补全功能
springboot集成ElasticSearch使用completion实现补全功能
27 1
|
17天前
|
XML 存储 Java
SpringBoot集成Flowable:构建强大的工作流引擎
在企业级应用开发中,工作流管理是核心功能之一。Flowable是一个开源的工作流引擎,它提供了BPMN 2.0规范的实现,并且与SpringBoot框架完美集成。本文将探讨如何使用SpringBoot和Flowable构建一个强大的工作流引擎,并分享一些实践技巧。
46 0
|
1月前
|
easyexcel Java UED
SpringBoot中大量数据导出方案:使用EasyExcel并行导出多个excel文件并压缩zip后下载
在SpringBoot环境中,为了优化大量数据的Excel导出体验,可采用异步方式处理。具体做法是将数据拆分后利用`CompletableFuture`与`ThreadPoolTaskExecutor`并行导出,并使用EasyExcel生成多个Excel文件,最终将其压缩成ZIP文件供下载。此方案提升了导出效率,改善了用户体验。代码示例展示了如何实现这一过程,包括多线程处理、模板导出及资源清理等关键步骤。
|
1月前
|
前端开发 Java 程序员
springboot 学习十五:Spring Boot 优雅的集成Swagger2、Knife4j
这篇文章是关于如何在Spring Boot项目中集成Swagger2和Knife4j来生成和美化API接口文档的详细教程。
109 1
|
1月前
|
Java Spring
springboot 学习十一:Spring Boot 优雅的集成 Lombok
这篇文章是关于如何在Spring Boot项目中集成Lombok,以简化JavaBean的编写,避免冗余代码,并提供了相关的配置步骤和常用注解的介绍。
105 0
|
1月前
|
JavaScript 前端开发 Java
Springboot+vue实现文件的下载和上传
这篇文章介绍了如何在Springboot和Vue中实现文件的上传和下载功能,包括后端控制器的创建、前端Vue组件的实现以及所需的依赖配置。
225 0
|
4月前
|
监控 druid Java
spring boot 集成配置阿里 Druid监控配置
spring boot 集成配置阿里 Druid监控配置
297 6
下一篇
无影云桌面