如题,我们在一些项目中可能会涉及到ftp上传、下载客户资料、附件。但是若每次上传或下载时都去创建一次ftp连接,上传( 下载)一个文件再关闭,则太耗费连接资源,这时候可以考虑使用连接池(就如同我们需要jdbc数据库连接池的道理一样)
笔者参考了网上的一些示例、技术文章,以及现有的apache commons-pool组件的代码等,自己封装了一套还算是比较完整的代码(此连接池底层依赖apache的commons-net和commons-pool2 组件) 。
此项目笔者已发布到github上了,项目地址:
https://github.com/jellyflu/ftpClientPool (对源码感兴趣的同学,请移步github哦 ~)
其实项目代码也不复杂,就是对apache ftp做了薄层的封装,总共才几个类,一千行代码不到。
FtpPoolConfig.java 继承自GenericObjectPoolConfig ,封装ftp的一些连接参数等。
package com.tingcream.ftpClientPool.config;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
/**
* ftp配置参数对象 继承自GenericObjectPoolConfig
*
* @author jelly
*
*/
public class FtpPoolConfig extends GenericObjectPoolConfig{
private String host;//主机名
private int port=21;//端口
private String username;//用户名
private String password;//密码
private int connectTimeOut=5000;//ftp 连接超时时间 毫秒
private String controlEncoding="utf-8";
private int bufferSize =1024;//缓冲区大小
private int fileType =2 ;// 传输数据格式 2表binary二进制数据
private int dataTimeout= 120000;
private boolean useEPSVwithIPv4 =false;
private boolean passiveMode =true;//是否启用被动模式
public int getConnectTimeOut() {
return connectTimeOut;
}
public void setConnectTimeOut(int connectTimeOut) {
this.connectTimeOut = connectTimeOut;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getControlEncoding() {
return controlEncoding;
}
public void setControlEncoding(String controlEncoding) {
this.controlEncoding = controlEncoding;
}
public int getBufferSize() {
return bufferSize;
}
public void setBufferSize(int bufferSize) {
this.bufferSize = bufferSize;
}
public int getFileType() {
return fileType;
}
public void setFileType(int fileType) {
this.fileType = fileType;
}
public int getDataTimeout() {
return dataTimeout;
}
public void setDataTimeout(int dataTimeout) {
this.dataTimeout = dataTimeout;
}
public boolean isUseEPSVwithIPv4() {
return useEPSVwithIPv4;
}
public void setUseEPSVwithIPv4(boolean useEPSVwithIPv4) {
this.useEPSVwithIPv4 = useEPSVwithIPv4;
}
public boolean isPassiveMode() {
return passiveMode;
}
public void setPassiveMode(boolean passiveMode) {
this.passiveMode = passiveMode;
}
}
FTPClientFactory.java ftp客户端连接创建工厂
package com.tingcream.ftpClientPool.core;
import java.io.IOException;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.log4j.Logger;
import com.tingcream.ftpClientPool.config.FtpPoolConfig;
/**
* ftpclient 工厂
* @author jelly
*
*/
public class FTPClientFactory extends BasePooledObjectFactory<FTPClient> {
private static Logger logger =Logger.getLogger(FTPClientFactory.class);
private FtpPoolConfig ftpPoolConfig;
public FtpPoolConfig getFtpPoolConfig() {
return ftpPoolConfig;
}
public void setFtpPoolConfig(FtpPoolConfig ftpPoolConfig) {
this.ftpPoolConfig = ftpPoolConfig;
}
/**
* 新建对象
*/
@Override
public FTPClient create() throws Exception {
FTPClient ftpClient = new FTPClient();
ftpClient.setConnectTimeout(ftpPoolConfig.getConnectTimeOut());
try {
logger.info("连接ftp服务器:" +ftpPoolConfig.getHost()+":"+ftpPoolConfig.getPort());
ftpClient.connect(ftpPoolConfig.getHost(), ftpPoolConfig.getPort());
int reply = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftpClient.disconnect();
logger.error("FTPServer 拒绝连接");
return null;
}
boolean result = ftpClient.login(ftpPoolConfig.getUsername(),ftpPoolConfig.getPassword());
if (!result) {
logger.error("ftpClient登录失败!");
throw new Exception("ftpClient登录失败! userName:"+ ftpPoolConfig.getUsername() + ", password:"
+ ftpPoolConfig.getPassword());
}
ftpClient.setControlEncoding(ftpPoolConfig.getControlEncoding());
ftpClient.setBufferSize(ftpPoolConfig.getBufferSize());
ftpClient.setFileType(ftpPoolConfig.getFileType());
ftpClient.setDataTimeout(ftpPoolConfig.getDataTimeout());
ftpClient.setUseEPSVwithIPv4(ftpPoolConfig.isUseEPSVwithIPv4());
if(ftpPoolConfig.isPassiveMode()){
logger.info("进入ftp被动模式");
ftpClient.enterLocalPassiveMode();//进入被动模式
}
} catch (IOException e) {
logger.error("FTP连接失败:", e);
}
return ftpClient;
}
@Override
public PooledObject<FTPClient> wrap(FTPClient ftpClient) {
return new DefaultPooledObject<FTPClient>(ftpClient);
}
/**
* 销毁对象
*/
@Override
public void destroyObject(PooledObject<FTPClient> p) throws Exception {
FTPClient ftpClient = p.getObject();
ftpClient.logout();
super.destroyObject(p);
}
/**
* 验证对象
*/
@Override
public boolean validateObject(PooledObject<FTPClient> p) {
FTPClient ftpClient = p.getObject();
boolean connect = false;
try {
connect = ftpClient.sendNoOp();
} catch (IOException e) {
e.printStackTrace();
}
return connect;
}
/**
* No-op.
*
* @param p ignored
*/
@Override
public void activateObject(PooledObject<FTPClient> p) throws Exception {
// The default implementation is a no-op.
}
/**
* No-op.
*
* @param p ignored
*/
@Override
public void passivateObject(PooledObject<FTPClient> p)
throws Exception {
// The default implementation is a no-op.
}
}
FTPClientPool.java FTP客户端连接池
package com.tingcream.ftpClientPool.core;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.impl.GenericObjectPool;
/**
* FTP 客户端连接池
* @author jelly
*
*/
public class FTPClientPool {
/**
* ftp客户端连接池
*/
private GenericObjectPool<FTPClient> pool;
/**
* ftp客户端工厂
*/
private FTPClientFactory clientFactory;
/**
* 构造函数中 注入一个bean
*
* @param clientFactory
*/
public FTPClientPool( FTPClientFactory clientFactory){
this.clientFactory=clientFactory;
pool = new GenericObjectPool<FTPClient>(clientFactory, clientFactory.getFtpPoolConfig());
}
public FTPClientFactory getClientFactory() {
return clientFactory;
}
public GenericObjectPool<FTPClient> getPool() {
return pool;
}
/**
* 借 获取一个连接对象
* @return
* @throws Exception
*/
public FTPClient borrowObject() throws Exception {
FTPClient client = pool.borrowObject();
// if(!client.sendNoOp()){
// //使池中的对象无效
// client.logout();
// client.disconnect();
// pool.invalidateObject(client);
// client =clientFactory.create();
// pool.addObject();
// }
//
return client ;
}
/**
* 还 归还一个连接对象
* @param ftpClient
*/
public void returnObject(FTPClient ftpClient) {
if(ftpClient!=null){
pool.returnObject(ftpClient);
}
}
}
FTPClientHelper.java ftp客户端辅助类
package com.tingcream.ftpClientPool.client;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.net.ftp.FTPClient;
import com.tingcream.ftpClientPool.core.FTPClientPool;
import com.tingcream.ftpClientPool.util.ByteUtil;
/**
* ftp客户端辅助bean
*
* @author jelly
*
*/
public class FTPClientHelper {
private FTPClientPool ftpClientPool;
public void setFtpClientPool(FTPClientPool ftpClientPool) {
this.ftpClientPool = ftpClientPool;
}
/**
* 下载 remote文件流
* @param remote 远程文件
* @return 字节数据
* @throws Exception
*/
public byte[] retrieveFileStream(String remote) throws Exception {
FTPClient client=null;
InputStream in =null;
try {
// long start =System.currentTimeMillis();
client= ftpClientPool.borrowObject();
in=client.retrieveFileStream(remote);
// long end =System.currentTimeMillis();
// System.out.println("ftp下载耗时(毫秒):"+(end-start));
return ByteUtil.inputStreamToByteArray(in);
}finally{
if (in != null) {
in.close();
}
if (!client.completePendingCommand()) {
client.logout();
client.disconnect();
ftpClientPool.getPool().invalidateObject(client);
}
ftpClientPool.returnObject(client);
}
}
/**
* 创建目录 单个不可递归
* @param pathname 目录名称
* @return
* @throws Exception
*/
public boolean makeDirectory(String pathname) throws Exception {
FTPClient client=null;
try {
client= ftpClientPool.borrowObject();
return client.makeDirectory(pathname);
} finally{
ftpClientPool.returnObject(client);
}
}
/**
* 删除目录,单个不可递归
* @param pathname
* @return
* @throws IOException
*/
public boolean removeDirectory(String pathname) throws Exception {
FTPClient client=null;
try {
client= ftpClientPool.borrowObject();
return client.removeDirectory(pathname);
} finally{
ftpClientPool.returnObject(client);
}
}
/**
* 删除文件 单个 ,不可递归
* @param pathname
* @return
* @throws Exception
*/
public boolean deleteFile(String pathname) throws Exception {
FTPClient client=null;
try {
client= ftpClientPool.borrowObject();
return client.deleteFile(pathname);
}finally{
ftpClientPool.returnObject(client);
}
}
/**
* 上传文件
* @param remote
* @param local
* @return
* @throws Exception
*/
public boolean storeFile(String remote, InputStream local) throws Exception {
FTPClient client=null;
try {
client= ftpClientPool.borrowObject();
return client.storeFile(remote, local);
} finally{
ftpClientPool.returnObject(client);
}
}
}
ByteUtil字节工具类
package com.tingcream.ftpClientPool.util;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
/**
* 字节工具类
* @author jelly
*
*/
public class ByteUtil {
public static byte[] inputStreamToByteArray(InputStream in) {
try {
ByteArrayOutputStream out=new ByteArrayOutputStream();
byte[] buffer=new byte[1024*4];
int n=0;
while ( (n=in.read(buffer)) >0) {
out.write(buffer,0,n);
}
return out.toByteArray();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}