環(huán)境及版本:
- 框架:SpringMVC 5.1.7.RELEASE
commons-net 3.6
commons-pool 2 2.7.0
1. pom.xml中添加依賴
<!-- ftp -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>
<!-- 使用commons-pool2 實(shí)現(xiàn)ftp連接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.7.0</version>
</dependency>
2. 配置文件
#ftp服務(wù)器配置
ftp.host=192.168.241.128
ftp.port=21
ftp.username=ftp_user
ftp.password=123
#超時(shí)時(shí)間(0表示一直連接)
ftp.clientTimeout=0
ftp.connectTimeout=0
#編碼格式
ftp.encoding=UTF-8
#緩沖器大小
ftp.bufferSize=1024
#每次數(shù)據(jù)連接之前,ftp client告訴ftp server開(kāi)通一個(gè)端口來(lái)傳輸數(shù)據(jù)
ftp.passiveMode=true
#連接池?cái)?shù)量
ftp.defaultpoolsize=10
#FTP連接池配置
#最大數(shù)
ftpPool.maxTotal=50
#最小空閑
ftpPool.minIdle=0
#最大空閑
ftpPool.maxIdle=50
#最大等待時(shí)間
ftpPool.maxWait=-1
#池對(duì)象耗盡之后是否阻塞,maxWait<0時(shí)一直等待
ftpPool.blockWhenExhausted=true
#取對(duì)象是驗(yàn)證
ftpPool.testOnBorrow=true
#回收驗(yàn)證
ftpPool.testOnReturn=true
#創(chuàng)建時(shí)驗(yàn)證
ftpPool.testOnCreate=true
#空閑驗(yàn)證
ftpPool.testWhileIdle=false
#后進(jìn)先出
ftpPool.lifo=false
3. FtpClient工廠類
package com.longway.busi.component;
import org.apache.commons.net.ftp.FTP;
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 org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* @description:
* @title: FTP 工廠
* @author: lrxc
* @date: 2019/11/18 19:12
*/
@Component
public class FtpClientFactory extends BasePooledObjectFactory<FTPClient> {
@Value("${ftp.host}")
private String host;
@Value("${ftp.port}")
private int port;
@Value("${ftp.username}")
private String username;
@Value("${ftp.password}")
private String password;
@Value("${ftp.clientTimeout}")
private int clientTimeout;
@Value("${ftp.connectTimeout}")
private int connectTimeout;
@Value("${ftp.encoding}")
private String encoding;
@Value("${ftp.bufferSize}")
private int bufferSize;
@Value("${ftp.passiveMode}")
private boolean passiveMode;
private final static Logger log = Logger.getLogger(FtpClientFactory.class.getName());
/**
* 創(chuàng)建FtpClient對(duì)象
*/
@Override
public FTPClient create() {
FTPClient ftpClient = new FTPClient();
ftpClient.setConnectTimeout(connectTimeout);
try {
ftpClient.connect(host, port);
int replyCode = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(replyCode)) {
ftpClient.disconnect();
log.warn("FTPServer 連接失敗,replyCode: " + replyCode);
return null;
}
if (!ftpClient.login(username, password)) {
log.warn("ftpClient 登錄失?。?" + username + password);
return null;
}
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);//文件類型
ftpClient.setControlEncoding(encoding);
ftpClient.setBufferSize(bufferSize);
if (passiveMode) {
//這個(gè)方法的意思就是每次數(shù)據(jù)連接之前,ftp client告訴ftp server開(kāi)通一個(gè)端口來(lái)傳輸數(shù)據(jù)
ftpClient.enterLocalPassiveMode();
}
ftpClient.setSoTimeout(clientTimeout);
} catch (IOException e) {
log.error("FtpClient 創(chuàng)建錯(cuò)誤: " + e.toString());
}
return ftpClient;
}
/**
* 用PooledObject封裝對(duì)象放入池中
*/
@Override
public PooledObject<FTPClient> wrap(FTPClient ftpClient) {
return new DefaultPooledObject<>(ftpClient);
}
/**
* 銷毀FtpClient對(duì)象
*/
@Override
public void destroyObject(PooledObject<FTPClient> ftpPooled) {
if (ftpPooled == null) {
return;
}
FTPClient ftpClient = ftpPooled.getObject();
try {
if (ftpClient.isConnected()) {
ftpClient.logout();
}
} catch (Exception io) {
log.error("銷毀FtpClient錯(cuò)誤..." + io.toString());
} finally {
try {
ftpClient.disconnect();
} catch (IOException io) {
log.error("銷毀FtpClient錯(cuò)誤..." + io.toString());
}
}
}
/**
* 驗(yàn)證FtpClient對(duì)象
*/
@Override
public boolean validateObject(PooledObject<FTPClient> ftpPooled) {
try {
FTPClient ftpClient = ftpPooled.getObject();
return ftpClient.sendNoOp();
} catch (IOException e) {
log.error("驗(yàn)證FtpClient對(duì)象錯(cuò)誤: " + e.toString());
}
return false;
}
}
4. 創(chuàng)建連接池
package com.longway.busi.component;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* @description:
* @title: 官方FTP連接池
* @author: lrxc
* @date: 2019/12/03 19:09
*/
@Component
public class FtpClientPool {
@Value("${ftpPool.maxTotal}")
private int maxTotal;
@Value("${ftpPool.minIdle}")
private int minIdle;
@Value("${ftpPool.maxIdle}")
private int maxIdle;
@Value("${ftpPool.maxWait}")
private long maxWait;
@Value("${ftpPool.blockWhenExhausted}")
private boolean blockWhenExhausted;
@Value("${ftpPool.testOnBorrow}")
private boolean testOnBorrow;
@Value("${ftpPool.testOnReturn}")
private boolean testOnReturn;
@Value("${ftpPool.testOnCreate}")
private boolean testOnCreate;
@Value("${ftpPool.testWhileIdle}")
private boolean testWhileIdle;
@Value("${ftpPool.lifo}")
private boolean lifo;
//連接池
private GenericObjectPool<FTPClient> ftpClientPool;
@Autowired
private FtpClientFactory ftpClientFactory;
/**
* 初始化連接池
*/
@PostConstruct //加上該注解表明該方法會(huì)在bean初始化后調(diào)用
public void init() {
// 初始化對(duì)象池配置
GenericObjectPoolConfig<FTPClient> poolConfig = new GenericObjectPoolConfig<FTPClient>();
poolConfig.setBlockWhenExhausted(blockWhenExhausted);
poolConfig.setMaxWaitMillis(maxWait);
poolConfig.setMinIdle(minIdle);
poolConfig.setMaxIdle(maxIdle);
poolConfig.setMaxTotal(maxTotal);
poolConfig.setTestOnBorrow(testOnBorrow);
poolConfig.setTestOnReturn(testOnReturn);
poolConfig.setTestOnCreate(testOnCreate);
poolConfig.setTestWhileIdle(testWhileIdle);
poolConfig.setLifo(lifo);
// 初始化對(duì)象池
ftpClientPool = new GenericObjectPool<FTPClient>(ftpClientFactory, poolConfig);
}
public FTPClient borrowObject() throws Exception {
return ftpClientPool.borrowObject();
}
public void returnObject(FTPClient ftpClient) {
ftpClientPool.returnObject(ftpClient);
}
}
5. 封裝ftp上傳下載工具類
package com.longway.busi.component;
import org.apache.commons.io.IOUtils;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.net.io.CopyStreamAdapter;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.io.*;
/**
* @description:
* @title: 實(shí)現(xiàn)文件上傳下載
* @author: lrxc
* @date: 2019/11/19 14:09
*/
@Component
public class FtpClientTemplate {
private final static Logger log = Logger.getLogger(FtpClientTemplate.class.getName());
@Autowired
private FtpClientPool ftpClientPool;
/***
* 上傳Ftp文件
*
* @param localFile 本地文件路徑
* @param remotePath 上傳服務(wù)器路徑 - (/abc/1.txt)
* @return true or false
*/
public boolean uploadFile(File localFile, String remotePath) {
FTPClient ftpClient = null;
BufferedInputStream inStream = null;
try {
//從池中獲取對(duì)象
ftpClient = ftpClientPool.borrowObject();
// 驗(yàn)證FTP服務(wù)器是否登錄成功
int replyCode = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(replyCode)) {
log.warn("FTP服務(wù)器校驗(yàn)失敗, 上傳replyCode:{}" + replyCode+" "+localFile);
return false;
}
//切換到上傳目錄
if (!ftpClient.changeWorkingDirectory(remotePath)) {
//如果目錄不存在創(chuàng)建目錄
String[] dirs = remotePath.split("/");
String tempPath = "";
for (String dir : dirs) {
if (null == dir || "".equals(dir)) continue;
tempPath += "/" + dir;
if (!ftpClient.changeWorkingDirectory(tempPath)) {
if (!ftpClient.makeDirectory(tempPath)) {
return false;
} else {
ftpClient.changeWorkingDirectory(tempPath);
}
}
}
}
inStream = new BufferedInputStream(new FileInputStream(localFile));
//設(shè)置上傳文件的類型為二進(jìn)制類型
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
//嘗試上傳三次
for (int j = 0; j < 3; j++) {
//避免進(jìn)度回調(diào)過(guò)于頻繁
final int[] temp = {0};
//上傳進(jìn)度監(jiān)控
ftpClient.setCopyStreamListener(new CopyStreamAdapter() {
@Override
public void bytesTransferred(long totalBytesTransferred, int bytesTransferred, long streamSize) {
int percent = (int) (totalBytesTransferred * 100 / localFile.length());
if (temp[0] < percent) {
temp[0] = percent;
log.info("↑↑ 上傳進(jìn)度 " + percent + " " + localFile.getAbsolutePath());
}
}
});
boolean success = ftpClient.storeFile(localFile.getName(), inStream);
if (success) {
log.info("文件上傳成功! " + localFile.getName());
return true;
}
log.info("文件上傳失敗" + localFile.getName() + " 重試 " + j);
}
log.info("文件上傳多次仍失敗" + localFile.getName());
} catch (Exception e) {
log.error("文件上傳錯(cuò)誤! " + localFile.getName(), e);
} finally {
IOUtils.closeQuietly(inStream);
//將對(duì)象放回池中
ftpClientPool.returnObject(ftpClient);
}
return false;
}
/**
* 下載文件
*
* @param remotePath FTP服務(wù)器文件目錄
* @param fileName 需要下載的文件名稱
* @param localPath 下載后的文件路徑
* @return true or false
*/
public boolean downloadFile(String remotePath, String fileName, String localPath) {
FTPClient ftpClient = null;
OutputStream outputStream = null;
try {
ftpClient = ftpClientPool.borrowObject();
// 驗(yàn)證FTP服務(wù)器是否登錄成功
int replyCode = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(replyCode)) {
log.warn("FTP服務(wù)器校驗(yàn)失敗, 下載replyCode:{}" + replyCode + " " + localPath + "/" + fileName);
return false;
}
// 切換FTP目錄
ftpClient.changeWorkingDirectory(remotePath);
FTPFile[] ftpFiles = ftpClient.listFiles();
for (FTPFile file : ftpFiles) {
if (fileName.equalsIgnoreCase(file.getName())) {
//保存至本地路徑
File localFile = new File(localPath + "/" + file.getName());
//創(chuàng)建父級(jí)目錄
if (!localFile.getParentFile().exists()) {
localFile.getParentFile().mkdirs();
}
//嘗試下載三次
for (int i = 0; i < 3; i++) {
//避免進(jìn)度回調(diào)過(guò)于頻繁
final int[] temp = {0};
//下載進(jìn)度監(jiān)控
ftpClient.setCopyStreamListener(new CopyStreamAdapter() {
@Override
public void bytesTransferred(long totalBytesTransferred, int bytesTransferred, long streamSize) {
int percent = (int) (totalBytesTransferred * 100 / file.getSize());
if (temp[0] < percent) {
temp[0] = percent;
log.info(" ↓↓ 下載進(jìn)度 " + percent + " " + localFile.getAbsolutePath());
}
}
});
outputStream = new FileOutputStream(localFile);
boolean success = ftpClient.retrieveFile(file.getName(), outputStream);
outputStream.flush();
if (success) {
log.info("文件下載成功! " + localFile.getName());
return true;
}
log.info("文件下載失敗" + localFile.getName() + " 重試 " + i);
}
log.info("文件下載多次仍失敗" + localFile.getName());
}
}
} catch (Exception e) {
log.error("文件下載錯(cuò)誤! " + remotePath + "/" + fileName, e);
} finally {
IOUtils.closeQuietly(outputStream);
ftpClientPool.returnObject(ftpClient);
}
return false;
}
}
6. 測(cè)試類
package com.bxlt.test;
import com.bxlt.component.FtpClientTemplate;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.io.File;
@RunWith(SpringJUnit4ClassRunner.class)//用于在JUnit環(huán)境下提供Spring Test框架的功能。
@ContextConfiguration(locations = {"classpath*:*.xml"})//用來(lái)加載配置文件
public class FtpTest {
@Autowired
private FtpClientTemplate ftpClientTemplate;
@Test
public void download() {
ftpClientTemplate.downloadFile("/aaa", "gaofeng.tgz", "E:\\aaa");
}
@Test
public void upload() {
File file = new File("E:\\aaa\\gaofeng.tgz");
ftpClientTemplate.uploadFile(file, "/abc");
}
}
最后編輯于 :
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。