1. 程式人生 > >spring中使用ftp連線池(ftpClientPool)

spring中使用ftp連線池(ftpClientPool)

如題,我們在一些專案中可能會涉及到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;
		}  
	 }

}