1. 程式人生 > >ftp連線池客戶端

ftp連線池客戶端

package com.scenetec.isv.utils.ftp.core;

import com.scenetec.isv.utils.ftp.config.FtpClientProperties;
import lombok.extern.slf4j.Slf4j;
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.springframework.stereotype.Component;

/**
* @author
[email protected]

* @description ftpclient客戶端連線工廠類
* @date 2018/12/19
*/
@Slf4j
@Component
public class FtpClientFactory extends BasePooledObjectFactory<FTPClient> {

private FtpClientProperties config;

public FtpClientFactory(FtpClientProperties config) {
this.config = config;
}

@Override
public FTPClient create() {

FTPClient ftpClient = new FTPClient();
ftpClient.setControlEncoding(config.getEncoding());
if (config.getConnectTimeout() != null) {
ftpClient.setConnectTimeout(config.getConnectTimeout());
}

try {
ftpClient.connect(config.getHost(), config.getPort());
int replyCode = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(replyCode)) {
ftpClient.disconnect();
log.warn("FTPServer refused connection,replyCode:{}", replyCode);
return null;
}

if (!ftpClient.login(config.getUsername(), config.getPassword())) {
log.warn("FTPClient login failed... username is {}; password: {}", config.getUsername(), config.getPassword());
}

ftpClient.setBufferSize(config.getBufferSize());
ftpClient.setFileType(config.getTransferFileType());
if (config.isPassiveMode()) {
ftpClient.enterLocalPassiveMode();
}

} catch (Exception ex) {
ex.printStackTrace();
log.error("Failed to create FTP connection");
}

return ftpClient;
}

void destroyObject(FTPClient client) {
destroyObject(wrap(client));
}

/**
* 用PooledObject封裝物件放入池中
* @param ftpClient ftp客戶端
* @return 預設池物件
*/
@Override
public PooledObject<FTPClient> wrap(FTPClient ftpClient) {
return new DefaultPooledObject<>(ftpClient);
}

/**
* 銷燬FtpClient物件
* @param ftpPooled ftp池物件
*/
@Override
public void destroyObject(PooledObject<FTPClient> ftpPooled) {
if (ftpPooled == null) {
return;
}

FTPClient ftpClient = ftpPooled.getObject();

try {
if (ftpClient.isConnected()) {
ftpClient.logout();
}
} catch (Exception ex) {
ex.printStackTrace();
log.error("Failure to destroy FTP connection pool.");
} finally {
try {
ftpClient.disconnect();
} catch (Exception ex) {
ex.printStackTrace();
log.error("Failed to close FTP connection pool.");
}
}
}

/**
* 驗證FtpClient物件
* @param ftpPooled ftp池物件
* @return 傳送一個NOOP命令到FTP伺服器,如果成功返回true,否則為false。
*/
@Override
public boolean validateObject(PooledObject<FTPClient> ftpPooled) {
try {
FTPClient ftpClient = ftpPooled.getObject();
if (ftpClient == null) {
return false;
}
if (!ftpClient.isConnected()) {
return false;
}
return ftpClient.sendNoOp();
} catch (Exception ex) {
ex.printStackTrace();
}
return false;
}

}



package com.scenetec.isv.utils.ftp.core;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.BaseObjectPool;
import org.springframework.util.ObjectUtils;

import java.util.NoSuchElementException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

/**
* @author
[email protected]

* @description ftp客戶端連線池類
* @date 2018/12/19
*/
@Slf4j
@Deprecated
public class FtpClientPool extends BaseObjectPool<FTPClient> {

private static final int DEFAULT_POOL_SIZE = 5;
private final BlockingQueue<FTPClient> pool;
private final FtpClientFactory factory;

/**
* 初始化連線池,需要注入一個工廠來提供FTPClient例項
* @param factory
*/
public FtpClientPool(FtpClientFactory factory) {
this(DEFAULT_POOL_SIZE, factory);
}

public FtpClientPool(int poolSize, FtpClientFactory factory) {
this.factory = factory;
this.pool = new ArrayBlockingQueue<>(poolSize * 2);
initPool(poolSize);
}

/**
* 初始化連線池,需要注入一個工廠來提供FTPClient例項
* @param maxPoolSize
*/
private void initPool(int maxPoolSize) {
try {
for (int i = 0; i < maxPoolSize; i++) {
addObject();
}
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException("Failed to initialize FTP thread pool.");
}
}

/**
* 客戶端從池中借出一個物件
* @return
* @throws Exception
* @throws NoSuchElementException
* @throws IllegalStateException
*/
@Override
public FTPClient borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
FTPClient client = pool.take();
if (ObjectUtils.isEmpty(client)) {
// 建立新的連線
client = factory.create();
// 返換物件
returnObject(client);
}
// 驗證物件是否有效
else if (!factory.validateObject(factory.wrap(client))) {
// 對無效的物件進行處理
invalidateObject(client);
// 建立新的物件
client = factory.create();
// 將新的物件放入連線池
returnObject(client);
}
// 返回ftp物件
return client;
}

/**
* 返還物件到連線池中
* @param client
* @throws Exception
*/
@Override
public void returnObject(FTPClient client) {
try {
long timeout = 3L;
if (client != null) {
if (client.isConnected()) {
if (pool.size() < DEFAULT_POOL_SIZE) {
if (!pool.offer(client, timeout, TimeUnit.SECONDS)) {
factory.destroyObject(client);
}
} else {
factory.destroyObject(client);
}
} else {
factory.destroyObject(client);
}
}
} catch (Exception ex) {
ex.printStackTrace();
log.error("Failed to return FTP connection object.");
}
}

/**
* 移除無效的物件
* @param client
* @throws Exception
*/
@Override
public void invalidateObject(FTPClient client) {
try {
// 移除無效物件
pool.remove(client);
// 登出物件
factory.destroyObject(client);
} catch (Exception ex) {
ex.printStackTrace();
log.error("Failed to remove invalid FTP object.");
}
}

/**
* 增加一個新的連結,超時失效
* @throws Exception
* @throws IllegalStateException
* @throws UnsupportedOperationException
*/
@Override
public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException {
pool.offer(factory.create(), 3, TimeUnit.SECONDS);
}

@Override
public void close() {
try {
while (pool.iterator().hasNext()) {
FTPClient client = pool.take();
factory.destroyObject(client);
}
} catch (Exception ex) {
ex.printStackTrace();
log.error("Failed to close FTP object.");
}
}
}



package com.scenetec.isv.utils.ftp.core;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.springframework.stereotype.Component;

import java.io.*;

/**
* @author [email protected]
* @description ftpClient模板上傳下載檔案類
* @date 2018/12/20
*/
@Slf4j
@Component
public class FtpClientTemplate {

private GenericObjectPool<FTPClient> ftpClientPool;

public FtpClientTemplate(FtpClientFactory ftpClientFactory) {
this.ftpClientPool = new GenericObjectPool<>(ftpClientFactory);
}

/**
* 上傳檔案
*
* @param localPath 本地路徑
* @param remotePath 遠端路徑,必須包含檔名("/"為當前ftp使用者根路徑)
* @return 上傳成功返回true, 否則返回false
*/
public boolean uploadFile(String localPath, String remotePath) {
if (StringUtils.isBlank(localPath)) {
log.error("本地檔案路徑為空");
return false;
}
return uploadFile(new File(localPath), remotePath);
}

/**
* 上傳檔案
*
* @param localFile 本地檔案
* @param remotePath 遠端檔案,必須包含檔名
* @return 上傳成功返回true, 否則返回false
*/
public boolean uploadFile(File localFile, String remotePath) {
if (!localFile.exists()) {
log.error("本地檔案不存在");
return false;
}
if (!localFile.isFile()) {
log.error("上傳型別不是檔案");
}
if (StringUtils.isBlank(remotePath)) {
remotePath = "/";
}
FileInputStream fis = null;
BufferedInputStream bis = null;
try {
fis = new FileInputStream(localFile);
bis = new BufferedInputStream(fis);
// 上傳
return uploadFile(bis, remotePath);
} catch (FileNotFoundException fex) {
fex.printStackTrace();
log.error("系統找不到指定的檔案:{}", localFile);
} catch (Exception ex) {
ex.printStackTrace();
log.error("上傳檔案異常。");
} finally {
try {
if (bis != null) {
bis.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
try {
if (fis != null) {
fis.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
return false;
}

/**
* 上傳檔案
*
* @param fileContent 檔案內容
* @param remotePath 遠端檔案,必須包含檔名
* @return 上傳成功返回true, 否則返回false
*/
public boolean uploadFile(byte[] fileContent, String remotePath) {
if (fileContent == null || fileContent.length <= 0) {
log.error("上傳檔案內容為空。");
return false;
}
InputStream is = null;
try {
is = new ByteArrayInputStream(fileContent);
// 上傳
return uploadFile(is, remotePath);
} catch (Exception ex) {
ex.printStackTrace();
log.error("上傳檔案異常。原因:【{}】", ex.getMessage());
} finally {
try {
if (is != null) {
is.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
return false;
}

/**
* 上傳檔案
*
* @param inputStream 檔案流
* @param remotePath 遠端檔案,必須包含檔名
* @return 上傳成功返回true, 否則返回false
*/
public boolean uploadFile(InputStream inputStream, String remotePath) {
// 上傳
FTPClient client = null;
try {
// 獲取遠端檔案路徑
String remoteFilePath = getRemoteFilePath(remotePath);
// 獲取遠端檔名
String remoteFileName = getRemoteFileName(remotePath);
// 從池中獲取物件
client = getFtpClient();
// 驗證連線是否成功
boolean bool = FTPReply.isPositiveCompletion(client.getReplyCode());
if (!bool) {
log.error("連線伺服器失敗,{}", client.getReplyString());
return false;
}
// 切換工作路徑
bool = changeDirectory(client, remoteFilePath);
if (!bool) {
log.error("切換工作路徑失敗,{}", client.getReplyString());
return false;
}
// 設定重試次數
final int retryTime = 3;
boolean retryResult = false;

for (int i = 0; i <= retryTime; i++) {
boolean success = client.storeFile(remoteFileName, inputStream);
if (success) {
log.info("檔案【{}】上傳成功。", remotePath);
retryResult = true;
break;
} else {
log.error("檔案上傳失敗。{}", client.getReplyString());
}
log.warn("檔案【{}】上傳失敗,重試上傳...嘗試{}次", remotePath, i);
}

return retryResult;
} catch (Exception ex) {
ex.printStackTrace();
log.error("上傳檔案異常。");
} finally {
// 將物件放回池中
if (client != null) {
ftpClientPool.returnObject(client);
}
}

return false;
}

/**
* 下載檔案
* @param remotePath 遠端檔案,必須包含檔名
* @param localPath 本地路徑,必須包含檔名(全路徑)
* @return 下載成功返回true,否則返回false
*/
public boolean downloadFile(String remotePath, String localPath) {
if (StringUtils.isBlank(remotePath)) {
remotePath = "/";
}
if (StringUtils.isBlank(localPath)) {
log.error("本地檔案路徑為空");
return false;
}
// 建立本地檔案路徑
File localFile = new File(localPath);
if (!localFile.exists()) {
File parentFile = localFile.getParentFile();
if (!parentFile.exists()) {
boolean bool = parentFile.mkdirs();
if (!bool) {
log.error("建立本地路徑失敗");
return false;
}
}
}
// 下載
FTPClient client = null;
OutputStream os = null;
try {
os = new FileOutputStream(localPath);
// 獲取遠端檔案路徑
String remoteFilePath = getRemoteFilePath(remotePath);
// 獲取遠端檔名
String remoteFileName = getRemoteFileName(remotePath);
// 從池中獲取物件
client = getFtpClient();
// 驗證連線是否成功
boolean bool = FTPReply.isPositiveCompletion(client.getReplyCode());
if (!bool) {
log.error("連線伺服器失敗,{}", client.getReplyString());
return false;
}
// 切換工作路徑
bool = client.changeWorkingDirectory(remoteFilePath);
if (!bool) {
log.error("切換工作路徑失敗,{}", client.getReplyString());
return false;
}
// 設定重試次數
final int retryTime = 3;
boolean retryResult = false;

for (int i = 0; i <= retryTime; i++) {
boolean success = client.retrieveFile(remoteFileName, os);
if (success) {
log.info("檔案【{}】下載成功。", localPath);
retryResult = true;
break;
} else {
log.error("檔案下載失敗。 {}", client.getReplyString());
}
log.warn("檔案【{}】下載失敗,重試下載...嘗試{}次", localPath, i);
}
// 返回結果
return retryResult;
} catch (Exception ex) {
ex.printStackTrace();
log.error("下載檔案異常。");
} finally {
if (client != null) {
ftpClientPool.returnObject(client);
}
try {
if (os != null) {
os.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
return false;
}

/**
* 刪除檔案
* @param remotePath 遠端檔案,必須包含檔名
* @return 下載成功返回true,否則返回false
*/
public boolean deleteFile(String remotePath) {
FTPClient client = null;
try {
// 從池中獲取物件
client = getFtpClient();
// 驗證連線是否成功
boolean bool = FTPReply.isPositiveCompletion(client.getReplyCode());
if (!bool) {
log.error("連線伺服器失敗,{}", client.getReplyString());
return false;
}
// 刪除檔案
bool = client.deleteFile(remotePath);
if (!bool) {
log.error("刪除檔案失敗,{}", client.getReplyString());
}
return bool;
} catch (Exception ex) {
ex.printStackTrace();
log.error("刪除檔案異常。");
} finally {
if (client != null) {
ftpClientPool.returnObject(client);
}
}
return false;
}

/**
* 獲取遠端工作目錄
* @param remotePath 遠端路徑
* @return 返回工作路徑
*/
private String getRemoteFilePath(String remotePath) {
if (StringUtils.isNotBlank(remotePath)) {
return remotePath.substring(0, remotePath.lastIndexOf("/") + 1);
}
return "/";
}

/**
* 獲取遠端檔名
* @param remotePath 遠端路徑
* @return 返回檔名
*/
private String getRemoteFileName(String remotePath) {
if (StringUtils.isNotBlank(remotePath)) {
return remotePath.substring(remotePath.lastIndexOf("/") + 1);
}
return "";
}

/**
* 驗證連線是否成功
* @return 連線登入成功返回true,否則返回false
*/
private FTPClient getFtpClient () {
FTPClient client = null;
try {
while (true) {
// 獲取客戶端
client = ftpClientPool.borrowObject();
// 驗證客戶端
if (client == null) {
continue;
} else {
if (!client.isConnected() || !FTPReply.isPositiveCompletion(client.getReplyCode())) {
ftpClientPool.invalidateObject(client);
} else {
break;
}
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
return client;
}

/**
* 切換工作目錄
* @param client ftp客戶端
* @param dir 工作目錄
* @return 切換成功返回true,否則返回false
*/
private boolean changeDirectory(FTPClient client, String dir) {
try {
if (client == null || StringUtils.isBlank(dir)) {
return false;
}

String fileBackslashSeparator = "\\";
String fileSeparator = "/";

if (StringUtils.contains(dir, fileBackslashSeparator)) {
dir = StringUtils.replaceAll(dir, fileBackslashSeparator, fileSeparator);
}

String[] dirArray = StringUtils.split(dir, fileSeparator);

String tmp = "";
for (String aDirArray : dirArray) {
tmp += fileSeparator + aDirArray;
if (!client.changeWorkingDirectory(tmp)) {
// 建立工作目錄
client.makeDirectory(tmp);
// 切換工作目錄
client.changeWorkingDirectory(tmp);
}
}
return true;
} catch (Exception ex) {
ex.printStackTrace();
log.error("切換工作目錄失敗。");
}
return false;
}

}