commons-net 包介绍
commons-net 是 apachecommons 用于网络的工具包,它实现了一些常见的网络工具,如 smtp
,pop3
,telnet
,ftp
,udp
等,本文主要使用它的 ftp 工具。
使用 FTP 工具时的问题
在使用 commons-net 提供的 ftp 工具的时候 ,发现每次都要走一遍完整的连接,登录流程。每次都创建连接的话,很快就会把连接耗光,如果使用单例,则效率过低,因为只有一个连接,所以考虑用对象池的方式。
自已定义对象池的话,我之前有弄过,但要考虑好多的问题。像线程池一样,需要考虑核心对象数、最大对象数、何时创建对象 、及队列等,这时可以使用 apache 的 commons-pool2 来做一个对象池。
重要说明 : FTPClient 每次使用都需要重新连接,不然它会自动断开连接,使用会直接返回 421 ,本文章只是给个使用 commons-pool2 对象池的示例。
如何使用 commons-pool2
可以这么想,如果我要做个对象池的工具给别人用,首先要考虑的是池子里装的什么,用户要如何创建池子里的对象,然后提供方法借对象和回收对象,我可以把池子中的对象抽象出来,由一个工厂统一管理,用户的对象从我的通用对象继承或实现,或使用聚合方式 。
其实 spring-data-redis 已经给我们一个完整的使用 commons-pool2 的例子,它就是用的 commons-pool2 ,它的池中对象是 redis 连接,有兴趣可以去瞧瞧。
定义池中对象 FTPClient 的扩展对象
因为 FTPClient 的功能太过简单,连多层目录时自己创建目录都不会,所以有必要给它包装一下,这里你可以扩展常用到的方法。
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
@Slf4j
public class FtpClientExtend {
private FTPClient ftpClient ;
public FtpClientExtend(FTPClient ftpClient) {
this.ftpClient = ftpClient;
}
/**
* 列出文件列表
* @param filePath
* @return
* @throws IOException
*/
public FTPFile[] listFiles(String filePath) throws IOException {
return ftpClient.listFiles(filePath);
}
/**
* 下载文件
* @param filePath
* @return
*/
public InputStream downloadFile(String filePath) throws IOException {
return ftpClient.retrieveFileStream(filePath);
}
/**
* 存储文件
* @param s
* @param inputStream
*/
public void uploadFile(String filePath, InputStream inputStream) throws IOException {
File targetFilePath = new File(filePath);
Path path = targetFilePath.getParentFile().toPath();
Iterator<Path> iterator = path.iterator();
StringBuffer root = new StringBuffer("");
while (iterator.hasNext()){
Path next = iterator.next();
root.append("/").append(next);
//尝试切入目录
boolean success = ftpClient.changeWorkingDirectory(root.toString());
if(!success){
int mkd = ftpClient.mkd(next.toString());
ftpClient.changeWorkingDirectory(root.toString());
}
}
ftpClient.enterLocalPassiveMode();
ftpClient.setControlEncoding("UTF-8");
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
boolean storeFileResult = ftpClient.storeFile(targetFilePath.getName(), inputStream);
if (storeFileResult) {
log.debug("上传文件:" + filePath + ",到目录:" + ftpClient.printWorkingDirectory() + " 成功");
}else{
log.debug("上传文件:" + filePath + ",到目录:" + ftpClient.printWorkingDirectory() + " 失败");
}
}
}
使用聚合包裹池中对象
这个包裹对象的类才是工厂真正产生在池中的类,文末给出图示
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
public class FtpClientPool extends GenericObjectPool<FtpClientExtend> {
public FtpClientPool(PooledObjectFactory<FtpClientExtend> factory) {
super(factory);
}
public FtpClientPool(PooledObjectFactory<FtpClientExtend> factory, GenericObjectPoolConfig config) {
super(factory, config);
}
}
建立创建对象的工厂
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
public class FtpClientFactory extends BasePooledObjectFactory<FtpClientExtend> {
@Value("${ftp.host:localhost}")
private String host;
@Value("${ftp.port:21}")
private int port;
@Value("${ftp.username:ftpadmin}")
private String username;
@Value("${ftp.password:salt202}")
private String password;
@Override
public FtpClientExtend create() throws Exception {
FTPClient ftpClient = new FTPClient();
ftpClient.connect(host,port);
boolean login = ftpClient.login(username, password);
if(!login){
throw new RuntimeException("ftp 登录失败,检查用户名密码是否正确["+host+":"+port+"]["+username+"]["+password+"]");
}
return new FtpClientExtend(ftpClient);
}
@Override
public PooledObject<FtpClientExtend> wrap(FtpClientExtend ftpClientExtend) {
return new DefaultPooledObject(ftpClientExtend);
}
}
使用方法
@Autowired
private FtpClientPool ftpClientPool;
public void method(){
FtpClientExtend ftpClientExtend = null;
try{
ftpClientExtend = ftpClientPool.borrowObject();
}finally{
if(ftpClientExtend != null) {
ftpClientPool.returnObject(ftpClientExtend);
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。