最近在写一个FTP上传工具,用到了Apache的FTPClient,但是每个线程频繁的创建和销毁FTPClient对象对服务器的压力很大,因此,此处最好使用一个FTPClient连接池。仔细翻了一下Apache的api,发现它并没有一个FTPClientPool的实现,所以,不得不自己写一个FTPClientPool。下面就大体介绍一下开发连接池的整个过程,供大家参考。

我们可以利用Apache提供的common-pool包来协助我们开发连接池。而开发一个简单的对象池,仅需要实现common-pool 包中的ObjectPool和PoolableObjectFactory两个接口即可。

线程池的意义

为了减少频繁创建、销毁对象带来的性能消耗,我们可以利用对象池的技术来实现对象的复用。对象池提供了一种机制,它可以管理对象池中对象的生命周期,提供了获取和释放对象的方法,可以让客户端很方便的使用对象池中的对象。

pom引入依赖

commons-net

commons-net

3.5

commons-pool

commons-pool

1.6

org.apache.commons

commons-pool2

2.0

创建ftp配置信息

在resources目录下创建ftp.properties配置文件,目录结构如下:

添加如下的配置信息:

########### FTP用户名称 ###########

ftp.userName=hrabbit

########### FTP用户密码 ###########

ftp.passWord=123456

########### FTP主机IP ###########

ftp.host=127.0.0.1

########### FTP主机端口号 ###########

ftp.port=21

########### 保存根路径 ###########

ftp.baseUrl=/

创建FTPProperties.java配置文件

加载配置内容到Spring中,配置信息基本延用我的就可以。

/**

* FTP的配置信息

* @Auther: hrabbit

* @Date: 2018-12-03 2:06 PM

* @Description:

*/

@Data

@Component

@PropertySource("classpath:ftp.properties")

@ConfigurationProperties(prefix = "ftp")

public class FTPProperties {

private String username;

private String password;

private String host;

private Integer port;

private String baseUrl;

private Integer passiveMode = FTP.BINARY_FILE_TYPE;

private String encoding="UTF-8";

private int clientTimeout=120000;

private int bufferSize;

private int transferFileType=FTP.BINARY_FILE_TYPE;

private boolean renameUploaded;

private int retryTime;

}

创建FTPClientPool线程池

/**

* 自定义实现ftp连接池

* @Auther: hrabbit

* @Date: 2018-12-03 3:40 PM

* @Description:

*/

@Slf4j

@SuppressWarnings("all")

public class FTPClientPool implements ObjectPool {

private static final int DEFAULT_POOL_SIZE = 10;

public BlockingQueue blockingQueue;

private FTPClientFactory factory;

public FTPClientPool(FTPClientFactory factory) throws Exception {

this(DEFAULT_POOL_SIZE, factory);

}

public FTPClientPool(int poolSize, FTPClientFactory factory) throws Exception {

this.factory = factory;

this.blockingQueue = new ArrayBlockingQueue(poolSize);

initPool(poolSize);

}

/**

* 初始化连接池

* @param maxPoolSize

* 最大连接数

* @throws Exception

*/

private void initPool(int maxPoolSize) throws Exception {

int count = 0;

while(count < maxPoolSize) {

this.addObject();

count++;

}

}

/**

* 从连接池中获取对象

*/

@Override

public FTPClient borrowObject() throws Exception {

FTPClient client = blockingQueue.take();

if(client == null) {

client = factory.makeObject();

} else if(!factory.validateObject(client)) {

invalidateObject(client);

client = factory.makeObject();

}

return client;

}

/**

* 返还一个对象(链接)

*/

@Override

public void returnObject(FTPClient client) throws Exception {

if ((client != null) && !blockingQueue.offer(client,2,TimeUnit.MINUTES)) {

try {

factory.destroyObject(client);

} catch (Exception e) {

throw e;

}

}

}

/**

* 移除无效的对象(FTP客户端)

*/

@Override

public void invalidateObject(FTPClient client) throws Exception {

blockingQueue.remove(client);

}

/**

* 增加一个新的链接,超时失效

*/

@Override

public void addObject() throws Exception {

blockingQueue.offer(factory.makeObject(), 2, TimeUnit.MINUTES);

}

/**

* 重新连接

*/

public FTPClient reconnect() throws Exception {

return factory.makeObject();

}

/**

* 获取空闲链接数(这里暂不实现)

*/

@Override

public int getNumIdle() {

return blockingQueue.size();

}

/**

* 获取正在被使用的链接数

*/

@Override

public int getNumActive() {

return DEFAULT_POOL_SIZE - getNumIdle();

}

@Override

public void clear() throws Exception {

}

/**

* 关闭连接池

*/

@Override

public void close() {

try {

while(blockingQueue.iterator().hasNext()) {

FTPClient client = blockingQueue.take();

factory.destroyObject(client);

}

} catch(Exception e) {

log.error("close ftp client pool failed...{}", e);

}

}

/**

* 增加一个新的链接,超时失效

*/

public void addObject(FTPClient ftpClient) throws Exception {

blockingQueue.put(ftpClient);

}

}

创建一个FTPClientFactory工厂类

创建FTPClientFactory实现PoolableObjectFactory的接口,FTPClient工厂类,通过FTPClient工厂提供FTPClient实例的创建和销毁

/**

* FTPClient 工厂

* @Auther: hrabbit

* @Date: 2018-12-03 3:41 PM

* @Description:

*/

@Slf4j

@SuppressWarnings("all")

public class FTPClientFactory implements PoolableObjectFactory {

private FTPProperties ftpProperties;

public FTPClientFactory(FTPProperties ftpProperties) {

this.ftpProperties = ftpProperties;

}

@Override

public FTPClient makeObject() throws Exception {

FTPClient ftpClient = new FTPClient();

ftpClient.setControlEncoding(ftpProperties.getEncoding());

ftpClient.setConnectTimeout(ftpProperties.getClientTimeout());

try {

ftpClient.connect(ftpProperties.getHost(), ftpProperties.getPort());

int reply = ftpClient.getReplyCode();

if (!FTPReply.isPositiveCompletion(reply)) {

ftpClient.disconnect();

log.warn("FTPServer refused connection");

return null;

}

boolean result = ftpClient.login(ftpProperties.getUsername(), ftpProperties.getPassword());

ftpClient.setFileType(ftpProperties.getTransferFileType());

if (!result) {

log.warn("ftpClient login failed... username is {}", ftpProperties.getUsername());

}

} catch (Exception e) {

log.error("create ftp connection failed...{}", e);

throw e;

}

return ftpClient;

}

@Override

public void destroyObject(FTPClient ftpClient) throws Exception {

try {

if(ftpClient != null && ftpClient.isConnected()) {

ftpClient.logout();

}

} catch (Exception e) {

log.error("ftp client logout failed...{}", e);

throw e;

} finally {

if(ftpClient != null) {

ftpClient.disconnect();

}

}

}

@Override

public boolean validateObject(FTPClient ftpClient) {

try {

return ftpClient.sendNoOp();

} catch (Exception e) {

log.error("Failed to validate client: {}");

}

return false;

}

@Override

public void activateObject(FTPClient obj) throws Exception {

//Do nothing

}

@Override

public void passivateObject(FTPClient obj) throws Exception {

//Do nothing

}

}

创建FTPUtils.java的工具类

FTPUtils.java中封装了上传、下载等方法,在项目启动的时候,在@PostConstruct注解的作用下通过执行init()的方法,创建FTPClientFactory工厂中,并初始化了FTPClientPool线程池,这样每次调用方法的时候,都直接从FTPClientPool中取出一个FTPClient对象

/**

* @Auther: hrabbit

* @Date: 2018-12-03 3:47 PM

* @Description:

*/

@Slf4j

@Component

public class FTPUtils {

/**

* FTP的连接池

*/

@Autowired

public static FTPClientPool ftpClientPool;

/**

* FTPClient对象

*/

public static FTPClient ftpClient;

private static FTPUtils ftpUtils;

@Autowired

private FTPProperties ftpProperties;

/**

* 初始化设置

* @return

*/

@PostConstruct

public boolean init() {

FTPClientFactory factory = new FTPClientFactory(ftpProperties);

ftpUtils = this;

try {

ftpClientPool = new FTPClientPool(factory);

} catch (Exception e) {

e.printStackTrace();

return false;

}

return true;

}

/**

* 获取连接对象

* @return

* @throws Exception

*/

public static FTPClient getFTPClient() throws Exception {

//初始化的时候从队列中取出一个连接

if (ftpClient==null) {

synchronized (ftpClientPool) {

ftpClient = ftpClientPool.borrowObject();

}

}

return ftpClient;

}

/**

* 当前命令执行完成命令完成

* @throws IOException

*/

public void complete() throws IOException {

ftpClient.completePendingCommand();

}

/**

* 当前线程任务处理完成,加入到队列的最后

* @return

*/

public void disconnect() throws Exception {

ftpClientPool.addObject(ftpClient);

}

/**

* Description: 向FTP服务器上传文件

*

* @Version1.0

* @param remoteFile

* 上传到FTP服务器上的文件名

* @param input

* 本地文件流

* @return 成功返回true,否则返回false

*/

public static boolean uploadFile(String remoteFile, InputStream input) {

boolean result = false;

try {

getFTPClient();

ftpClient.enterLocalPassiveMode();

result = ftpClient.storeFile(remoteFile, input);

input.close();

ftpClient.disconnect();

} catch (Exception e) {

e.printStackTrace();

}

return result;

}

/**

* Description: 向FTP服务器上传文件

*

* @Version1.0

* @param remoteFile

* 上传到FTP服务器上的文件名

* @param localFile

* 本地文件

* @return 成功返回true,否则返回false

*/

public static boolean uploadFile(String remoteFile, String localFile){

FileInputStream input = null;

try {

input = new FileInputStream(new File(localFile));

} catch (FileNotFoundException e) {

e.printStackTrace();

}

return uploadFile(remoteFile, input);

}

/**

* 拷贝文件

* @param fromFile

* @param toFile

* @return

* @throws Exception

*/

public boolean copyFile(String fromFile, String toFile) throws Exception {

InputStream in=getFileInputStream(fromFile);

getFTPClient();

boolean flag = ftpClient.storeFile(toFile, in);

in.close();

return flag;

}

/**

* 获取文件输入流

* @param fileName

* @return

* @throws IOException

*/

public static InputStream getFileInputStream(String fileName) throws Exception {

ByteArrayOutputStream fos=new ByteArrayOutputStream();

getFTPClient();

ftpClient.retrieveFile(fileName, fos);

ByteArrayInputStream in=new ByteArrayInputStream(fos.toByteArray());

fos.close();

return in;

}

/**

* Description: 从FTP服务器下载文件

*

* @Version1.0

* @return

*/

public static boolean downFile(String remoteFile, String localFile){

boolean result = false;

try {

getFTPClient();

OutputStream os = new FileOutputStream(localFile);

ftpClient.retrieveFile(remoteFile, os);

ftpClient.logout();

ftpClient.disconnect();

result = true;

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

} catch (Exception e) {

e.printStackTrace();

}

}

return result;

}

/**

* 从ftp中获取文件流

* @param filePath

* @return

* @throws Exception

*/

public static InputStream getInputStream(String filePath) throws Exception {

getFTPClient();

InputStream inputStream = ftpClient.retrieveFileStream(filePath);

return inputStream;

}

/**

* ftp中文件重命名

* @param fromFile

* @param toFile

* @return

* @throws Exception

*/

public boolean rename(String fromFile,String toFile) throws Exception {

getFTPClient();

boolean result = ftpClient.rename(fromFile,toFile);

return result;

}

/**

* 获取ftp目录下的所有文件

* @param dir

* @return

*/

public FTPFile[] getFiles(String dir) throws Exception {

getFTPClient();

FTPFile[] files = new FTPFile[0];

try {

files = ftpClient.listFiles(dir);

}catch (Throwable thr){

thr.printStackTrace();

}

return files;

}

/**

* 获取ftp目录下的某种类型的文件

* @param dir

* @param filter

* @return

*/

public FTPFile[] getFiles(String dir, FTPFileFilter filter) throws Exception {

getFTPClient();

FTPFile[] files = new FTPFile[0];

try {

files = ftpClient.listFiles(dir, filter);

}catch (Throwable thr){

thr.printStackTrace();

}

return files;

}

/**

* 创建文件夹

* @param remoteDir

* @return 如果已经有这个文件夹返回false

*/

public boolean makeDirectory(String remoteDir) throws Exception {

getFTPClient();

boolean result = false;

try {

result = ftpClient.makeDirectory(remoteDir);

} catch (IOException e) {

e.printStackTrace();

}

return result;

}

public boolean mkdirs(String dir) throws Exception {

boolean result = false;

if (null == dir) {

return result;

}

getFTPClient();

ftpClient.changeWorkingDirectory("/");

StringTokenizer dirs = new StringTokenizer(dir, "/");

String temp = null;

while (dirs.hasMoreElements()) {

temp = dirs.nextElement().toString();

//创建目录

ftpClient.makeDirectory(temp);

//进入目录

ftpClient.changeWorkingDirectory(temp);

result = true;

}

ftpClient.changeWorkingDirectory("/");

return result;

}

}

创建FtpClientTest.java测试类

上传一张图片到FTP服务器,并将文件重新命名为hrabbit.jpg,代码如下:

/**

* FtpClient测试

* @Auther: hrabbit

* @Date: 2018-12-21 9:14 PM

* @Description:

*/

@RunWith(SpringRunner.class)

@SpringBootTest

public class FtpClientTest {

/**

* 测试上传

*/

@Test

public void uploadFile(){

boolean flag = FTPUtils.uploadFile("hrabbit.jpg", "/Users/mrotaku/Downloads/klklklkl_4x.jpg");

Assert.assertEquals(true, flag);

}

}

程序完美运行,这时候我们查看我们的FTP服务器,http://localhost:8866/hrabbit.jpg

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

java ftpclient多线程_Spring Boot整合FTPClient线程池的实现示例相关推荐

  1. async spring 默认线程池_Spring boot注解@Async线程池实例详解

    这篇文章主要介绍了Spring boot注解@Async线程池实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 从Spring3开始提供了@A ...

  2. java dsl框架_Spring Boot整合QueryDSL的实现示例

    之前研究Jooq,今天来研究一下搭配JPA的QueryDSL吧. 简介 Querydsl是一个Java开源框架用于构建类型安全的SQL查询语句.它采用API代替拼凑字符串来构造查询语句.可跟 Hibe ...

  3. 【java】多线程控制(二)- - -线程池

    线程池的分类 线程池分类 解释 newFixedThreadPool 固定大小 newCachedThreadPool 动态分配线程数目 newSingleThreadExecutor 单例线程,线程 ...

  4. druid java直接调用_Spring Boot使用Druid连接池的示例代码

    Druid是Java语言中最好的数据库连接池.Druid相比于其他的数据库连接池,有两大特性: 监控数据库,有利于分析线上数据库问题 更容易扩展,同时也很高效. 今天演示一下Spring Boot集成 ...

  5. Java多线程学习(八)线程池与Executor 框架

    历史优质文章推荐: Java并发编程指南专栏 分布式系统的经典基础理论 可能是最漂亮的Spring事务管理详解 面试中关于Java虚拟机(jvm)的问题看这篇就够了 目录: [TOC] 本节思维导图: ...

  6. android 多线程下载,断点续传,线程池

    android 多线程下载,断点续传,线程池 你可以在这里看到这个demo的源码: https://github.com/onlynight/MultiThreadDownloader 效果图 这张效 ...

  7. Java并发(二十一):线程池实现原理

    一.总览 线程池类ThreadPoolExecutor的相关类需要先了解: (图片来自:https://javadoop.com/post/java-thread-pool#%E6%80%BB%E8% ...

  8. Java Executor源码解析(3)—ThreadPoolExecutor线程池execute核心方法源码【一万字】

    基于JDK1.8详细介绍了ThreadPoolExecutor线程池的execute方法源码! 上一篇文章中,我们介绍了:Java Executor源码解析(2)-ThreadPoolExecutor ...

  9. Java Executor源码解析(7)—Executors线程池工厂以及四大内置线程池

    详细介绍了Executors线程池工具类的使用,以及四大内置线程池. 系列文章: Java Executor源码解析(1)-Executor执行框架的概述 Java Executor源码解析(2)-T ...

最新文章

  1. Metasploit advanced命令使用技巧
  2. Hadoop HDFS概念学习系列之熟练掌握HDFS的Shell访问(十五)
  3. 通过Java执行python文件
  4. FastDFS 学习笔记
  5. (转)简单代码生成器原理剖析(二)
  6. JS window对象 Location对象 location用于获取或设置窗体的URL,并且可以用于解析URL。 语法: location.[属性|方法]...
  7. java调用sqlserver存储过程_Java中调用SQLServer存储过程示例
  8. 将对象绑定到WinForm中的combobox时出现的奇怪错误:组合框的下拉项太多!
  9. httpd2.4.39直接访问路径下的ceb文件,显示乱码
  10. 台式计算机如何连接投影仪,台式机PC怎样与投影仪连接
  11. 合同计算问题的计算公式与计算方法
  12. 教资科目二重点简答题总结
  13. 移动端H5页面必用代码
  14. 领导最不赏识这5类下属
  15. 编程语言 - 强弱/动静态类型 - 整理
  16. 优麒麟 19.04 即将发布,华为、阿里云、重大、360四大境像站鼎力支持!
  17. Keithley 26xxB双通道源表 FET测试软件 field-effct-transister
  18. 工业机器人测试认证标准
  19. ILRuntime来实现热更新的优与劣!
  20. 虚幻4 读取Json文件数据

热门文章

  1. Surface Pro双指三指触摸手势失效解决方法
  2. 美团面试都面不过?我又不是去送外卖的!美团Java面试经历总结【一面、二面、三面】
  3. 国产“天价手机”凉凉,还记得8868吗?
  4. vc不支援此种界接口_今夏大热VC美白精华合集评测,变白不止一点点
  5. (转)哑铃健身图解大全
  6. html 块状元素转换,块状元素与内联元素的转换
  7. SAP里面的销售税是如何确定的
  8. 单片机比赛准备01-蓝桥杯-CT107D硬件开发平台熟悉
  9. Android设备wifi开发
  10. 贪心算法每日一题(3)