依賴的包
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.1</version>
</dependency>
方法示例
package com.xsh.util;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* FTP下載工具
* @author shuangxie
* @date 2019/5/24
*/
@Component
public class FTPUtil {
/**
* 日志對象
**/
private static final Logger logger = LoggerFactory.getLogger(FTPUtil.class);
/**
* 該目錄不存在
*/
public static final String DIR_NOT_EXIST = "該目錄不存在";
/**
* "該目錄下沒有文件
*/
public static final String DIR_CONTAINS_NO_FILE = "該目錄下沒有文件";
/**
* FTP地址
**/
@Value("${ftp.host}")
private String ftpAddress;
/**
* FTP端口
**/
@Value("${ftp.port}")
private int ftpPort = 521;
/**
* FTP用戶名
**/
@Value("${ftp.username}")
private String ftpUsername;
/**
* FTP密碼
**/
@Value("${ftp.password}")
private String ftpPassword;
/**
* FTP基礎目錄
**/
@Value("${ftp.basepath}")
private String basePath;
/**
* 本地字符編碼
**/
private static String localCharset = "GBK";
/**
* FTP協議里面,規定文件名編碼為iso-8859-1
**/
private static String serverCharset = "ISO-8859-1";
/**
* UTF-8字符編碼
**/
private static final String CHARSET_UTF8 = "UTF-8";
/**
* OPTS UTF8字符串常量
**/
private static final String OPTS_UTF8 = "OPTS UTF8";
/**
* 設置緩沖區大小4M
**/
private static final int BUFFER_SIZE = 1024 * 1024 * 4;
/**
* FTPClient對象
**/
private static FTPClient ftpClient = null;
/**
* 下載該目錄下所有文件到本地
*
* @param ftpPath FTP服務器上的相對路徑,例如:test/123
* @param savePath 保存文件到本地的路徑,例如:D:/test
* @return 成功返回true,否則返回false
*/
public boolean downloadFiles(String ftpPath, String savePath) {
// 登錄
login(ftpAddress, ftpPort, ftpUsername, ftpPassword);
if (ftpClient != null) {
try {
String path = changeEncoding(basePath + ftpPath);
// 判斷是否存在該目錄
if (!ftpClient.changeWorkingDirectory(path)) {
logger.error(basePath + ftpPath + DIR_NOT_EXIST);
return Boolean.FALSE;
}
ftpClient.enterLocalPassiveMode(); // 設置被動模式,開通一個端口來傳輸數據
String[] fs = ftpClient.listNames();
// 判斷該目錄下是否有文件
if (fs == null || fs.length == 0) {
logger.error(basePath + ftpPath + DIR_CONTAINS_NO_FILE);
return Boolean.FALSE;
}
for (String ff : fs) {
String ftpName = new String(ff.getBytes(serverCharset), localCharset);
File file = new File(savePath + '/' + ftpName);
try (OutputStream os = new FileOutputStream(file)) {
ftpClient.retrieveFile(ff, os);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
} catch (IOException e) {
logger.error("下載文件失敗", e);
} finally {
closeConnect();
}
}
return Boolean.TRUE;
}
/**
* 連接FTP服務器
*
* @param address 地址,如:127.0.0.1
* @param port 端口,如:21
* @param username 用戶名,如:root
* @param password 密碼,如:root
*/
private void login(String address, int port, String username, String password) {
ftpClient = new FTPClient();
try {
ftpClient.connect(address, port);
ftpClient.login(username, password);
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
//限制緩沖區大小
ftpClient.setBufferSize(BUFFER_SIZE);
int reply = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
closeConnect();
logger.error("FTP服務器連接失敗");
}
} catch (Exception e) {
logger.error("FTP登錄失敗", e);
}
}
/**
* FTP服務器路徑編碼轉換
*
* @param ftpPath FTP服務器路徑
* @return String
*/
private static String changeEncoding(String ftpPath) {
String directory = null;
try {
if (FTPReply.isPositiveCompletion(ftpClient.sendCommand(OPTS_UTF8, "ON"))) {
localCharset = CHARSET_UTF8;
}
directory = new String(ftpPath.getBytes(localCharset), serverCharset);
} catch (Exception e) {
logger.error("路徑編碼轉換失敗", e);
}
return directory;
}
/**
* 關閉FTP連接
*/
private void closeConnect() {
if (ftpClient != null && ftpClient.isConnected()) {
try {
ftpClient.logout();
ftpClient.disconnect();
} catch (IOException e) {
logger.error("關閉FTP連接失敗", e);
}
}
}
/**
* 檢查指定目錄下是否含有指定文件
*
* @param ftpPath FTP服務器文件相對路徑,例如:test/123
* @param fileName 要下載的文件名,例如:test.txt
* @return 成功返回true,否則返回false
*/
public boolean checkFileInFtp(String ftpPath, String fileName) {
// 登錄
login(ftpAddress, ftpPort, ftpUsername, ftpPassword);
if (ftpClient != null) {
try {
String path = changeEncoding(basePath + ftpPath);
// 判斷是否存在該目錄
if (!ftpClient.changeWorkingDirectory(path)) {
logger.error(basePath + ftpPath + DIR_NOT_EXIST);
return Boolean.FALSE;
}
ftpClient.enterLocalPassiveMode(); // 設置被動模式,開通一個端口來傳輸數據
String[] fs = ftpClient.listNames();
// 判斷該目錄下是否有文件
if (fs == null || fs.length == 0) {
logger.error(basePath + ftpPath + DIR_CONTAINS_NO_FILE);
return Boolean.FALSE;
}
for (String ff : fs) {
String ftpName = new String(ff.getBytes(serverCharset), localCharset);
if (ftpName.equals(fileName)) {
return Boolean.TRUE;
}
}
} catch (IOException e) {
logger.error("請求出錯", e);
} finally {
closeConnect();
}
}
return Boolean.TRUE;
}
/**
* 下載該目錄下所有文件到本地 根據實際需要修改執行邏輯
*
* @param ftpPath FTP服務器上的相對路徑,例如:test/123
* @param savePath 保存文件到本地的路徑,例如:D:/test
* @return 成功返回true,否則返回false
*/
public Map<String, Object> downLoadTableFile(String ftpPath, String savePath) {
// 登錄
login(ftpAddress, ftpPort, ftpUsername, ftpPassword);
Map<String, Object> resultMap = new HashMap<>();
if (ftpClient != null) {
try {
String path = changeEncoding(basePath + "/" + ftpPath);
// 判斷是否存在該目錄
if (!ftpClient.changeWorkingDirectory(path)) {
logger.error(basePath + "/" + ftpPath + DIR_NOT_EXIST);
resultMap.put("result", false);
return resultMap;
}
ftpClient.enterLocalPassiveMode(); // 設置被動模式,開通一個端口來傳輸數據
String[] fs = ftpClient.listNames();
// 判斷該目錄下是否有文件
if (fs == null || fs.length == 0) {
logger.error(basePath + "/" + ftpPath + DIR_CONTAINS_NO_FILE);
resultMap.put("result", false);
return resultMap;
}
List<String> tableFileNameList = new ArrayList<>();
//根據表名創建文件夾
String tableDirName = savePath + "/" + ftpPath;
File tableDirs=new File(tableDirName);
if(!tableDirs.exists()){
tableDirs.mkdirs();
}
for (String ff : fs) {
String ftpName = new String(ff.getBytes(serverCharset), localCharset);
File file = new File(tableDirName + "/" + ftpName);
//存儲文件名導入時使用
tableFileNameList.add(tableDirName + "/" + ftpName);
try (OutputStream os = new FileOutputStream(file)) {
ftpClient.retrieveFile(ff, os);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
resultMap.put("fileNameList", tableFileNameList);
resultMap.put("result", true);
return resultMap;
} catch (IOException e) {
logger.error("下載文件失敗", e);
} finally {
closeConnect();
}
}
resultMap.put("result", false);
return resultMap;
}
}
單元測試
@Autowired
private FTPUtil ftpUtil;
/**
* 本地存儲路徑
*/
@Value("${ftp.local.save.basepath}")
private String localSavePath;
@Test
public void ftpDownloadTest() {
Map<String, Object> result = ftpUtil.downLoadTableFile("dw_account_district", localSavePath);
TestCase.assertEquals(true, result.get("result"));
}
需要注意的點
- 需要設置緩沖區大小,防止下載過慢或者過快
- 需要確認ftp服務器的訪問模式,被動模式還是主動模式
- 需要注意編碼格式
源碼下載:https://github.com/xieshuang/util