这篇文章主要介绍了Spring Boot整合FTPClient线程池的实现示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
最近在写一个ftp上传工具,用到了apache的ftpclient,但是每个线程频繁的创建和销毁ftpclient对象对服务器的压力很大,因此,此处最好使用一个ftpclient连接池。仔细翻了一下apache的api,发现它并没有一个ftpclientpool的实现,所以,不得不自己写一个ftpclientpool。下面就大体介绍一下开发连接池的整个过程,供大家参考。
我们可以利用apache提供的common-pool包来协助我们开发连接池。而开发一个简单的对象池,仅需要实现common-pool 包中的objectpool和poolableobjectfactory两个接口即可。
线程池的意义
为了减少频繁创建、销毁对象带来的性能消耗,我们可以利用对象池的技术来实现对象的复用。对象池提供了一种机制,它可以管理对象池中对象的生命周期,提供了获取和释放对象的方法,可以让客户端很方便的使用对象池中的对象。
pom引入依赖<!-- ftpclient依赖包-->
<dependency>
<groupid>commons-net</groupid>
<artifactid>commons-net</artifactid>
<version>3.5</version>
</dependency>
<!-- 线程池-->
<dependency>
<groupid>commons-pool</groupid>
<artifactid>commons-pool</artifactid>
<version>1.6</version>
</dependency>
<dependency>
<groupid>org.apache.commons</groupid>
<artifactid>commons-pool2</artifactid>
<version>2.0</version>
</dependency> 创建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<ftpclient> {
private static final int default_pool_size = 10;
public blockingqueue<ftpclient> 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<ftpclient>(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<ftpclient> {
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
码云地址:https://gitee.com/hrabbit/hrabbit-admin
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持CodeAE代码之家。
原文链接:https://www.jianshu.com/p/6270b2308c4e
|