java ftpclient多线程_Spring Boot整合FTPClient线程池的实现示例
最近在写一个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线程池的实现示例相关推荐
- async spring 默认线程池_Spring boot注解@Async线程池实例详解
这篇文章主要介绍了Spring boot注解@Async线程池实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 从Spring3开始提供了@A ...
- java dsl框架_Spring Boot整合QueryDSL的实现示例
之前研究Jooq,今天来研究一下搭配JPA的QueryDSL吧. 简介 Querydsl是一个Java开源框架用于构建类型安全的SQL查询语句.它采用API代替拼凑字符串来构造查询语句.可跟 Hibe ...
- 【java】多线程控制(二)- - -线程池
线程池的分类 线程池分类 解释 newFixedThreadPool 固定大小 newCachedThreadPool 动态分配线程数目 newSingleThreadExecutor 单例线程,线程 ...
- druid java直接调用_Spring Boot使用Druid连接池的示例代码
Druid是Java语言中最好的数据库连接池.Druid相比于其他的数据库连接池,有两大特性: 监控数据库,有利于分析线上数据库问题 更容易扩展,同时也很高效. 今天演示一下Spring Boot集成 ...
- Java多线程学习(八)线程池与Executor 框架
历史优质文章推荐: Java并发编程指南专栏 分布式系统的经典基础理论 可能是最漂亮的Spring事务管理详解 面试中关于Java虚拟机(jvm)的问题看这篇就够了 目录: [TOC] 本节思维导图: ...
- android 多线程下载,断点续传,线程池
android 多线程下载,断点续传,线程池 你可以在这里看到这个demo的源码: https://github.com/onlynight/MultiThreadDownloader 效果图 这张效 ...
- Java并发(二十一):线程池实现原理
一.总览 线程池类ThreadPoolExecutor的相关类需要先了解: (图片来自:https://javadoop.com/post/java-thread-pool#%E6%80%BB%E8% ...
- Java Executor源码解析(3)—ThreadPoolExecutor线程池execute核心方法源码【一万字】
基于JDK1.8详细介绍了ThreadPoolExecutor线程池的execute方法源码! 上一篇文章中,我们介绍了:Java Executor源码解析(2)-ThreadPoolExecutor ...
- Java Executor源码解析(7)—Executors线程池工厂以及四大内置线程池
详细介绍了Executors线程池工具类的使用,以及四大内置线程池. 系列文章: Java Executor源码解析(1)-Executor执行框架的概述 Java Executor源码解析(2)-T ...
最新文章
- Metasploit advanced命令使用技巧
- Hadoop HDFS概念学习系列之熟练掌握HDFS的Shell访问(十五)
- 通过Java执行python文件
- FastDFS 学习笔记
- (转)简单代码生成器原理剖析(二)
- JS window对象 Location对象 location用于获取或设置窗体的URL,并且可以用于解析URL。 语法: location.[属性|方法]...
- java调用sqlserver存储过程_Java中调用SQLServer存储过程示例
- 将对象绑定到WinForm中的combobox时出现的奇怪错误:组合框的下拉项太多!
- httpd2.4.39直接访问路径下的ceb文件,显示乱码
- 台式计算机如何连接投影仪,台式机PC怎样与投影仪连接
- 合同计算问题的计算公式与计算方法
- 教资科目二重点简答题总结
- 移动端H5页面必用代码
- 领导最不赏识这5类下属
- 编程语言 - 强弱/动静态类型 - 整理
- 优麒麟 19.04 即将发布,华为、阿里云、重大、360四大境像站鼎力支持!
- Keithley 26xxB双通道源表 FET测试软件 field-effct-transister
- 工业机器人测试认证标准
- ILRuntime来实现热更新的优与劣!
- 虚幻4 读取Json文件数据