在企業級應用開發中,FTP(File Transfer Protocol)仍是檔案傳輸的常見選擇。Java 標準庫雖提供基礎網路功能,但處理 FTP 協定細節(如被動模式、二進位傳輸、斷點續傳)相對繁瑣。Apache Commons Net 函式庫封裝了完整的 FTP 客戶端實作,讓開發者能專注於業務邏輯而非協定細節。
本範例封裝了 FTPFileTransmit 類別,提供四大核心功能:
所有操作均採用「連線→登入→操作→斷線」的標準流程,並在 finally 區塊確保資源釋放:
FTPClient ftpClient = new FTPClient();
try {
ftpClient.connect(serverIP);
if (ftpClient.login(username, password)) {
// 業務操作
}
} finally {
IOUtils.closeQuietly(stream);
ftpClient.disconnect();
}
上傳前必須設定檔案類型為 BINARY_FILE_TYPE,並調整緩衝區大小(預設 1024 bytes),確保大檔案傳輸效能:
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
ftpClient.setBufferSize(1024);
使用 changeWorkingDirectory() 切換路徑,若目錄不存在則以 makeDirectory() 建立。範例中結合 SimpleDateFormat 產生「yyyyMMdd_ReTransmit」格式目錄名,實現按日期分流:
String folderName = new SimpleDateFormat("yyyyMMdd").format(new Date()) + "_ReTransmit";
ftpClient.makeDirectory(folderName);
getAllFolderNames() 方法示範如何列出遠端目錄並依名稱前綴(日期)篩選:
String[] allNames = ftpClient.listNames();
String threshold = minusTime(); // 取得兩天前日期字串
for (String name : allNames) {
if (name.substring(0, 8).compareTo(threshold) <= 0) {
// 標記為待刪除
}
}
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.io.IOUtils;
import org.apache.commons.net.ftp.FTPClient;
public class FTPFileTransmit {
private String ftpPath = "xxx/xxx/";
private String ftpName = "name";
private String ftpPassword = "pass";
private String ftpServerIP = "192.168.0.xx";
/**
* 上傳位元組陣列到 FTP
* @param folderName 遠端目錄(以 / 結尾)
* @param fileName 檔案名稱
* @param data 檔案位元組內容
* @return 是否成功
*/
public boolean saveInFTP(String folderName, String fileName, byte[] data) {
boolean flag = false;
FTPClient ftpClient = new FTPClient();
ByteArrayInputStream bis = null;
try {
if (isValid(folderName) && isValid(fileName)) {
ftpClient.connect(ftpServerIP);
if (ftpClient.login(ftpName, ftpPassword)) {
if (ftpClient.changeWorkingDirectory(folderName)) {
bis = new ByteArrayInputStream(data);
ftpClient.setBufferSize(1024);
if (ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE)) {
flag = ftpClient.storeFile(fileName, bis);
}
}
}
}
} catch (SocketException | IOException e) {
e.printStackTrace();
flag = false;
} finally {
IOUtils.closeQuietly(bis);
disconnectQuietly(ftpClient);
}
return flag;
}
/**
* 從 FTP 下載檔案到本地
* @return 是否成功
*/
public boolean getFromFTP() {
boolean flag = false;
FTPClient ftpClient = new FTPClient();
FileOutputStream fos = null;
try {
ftpClient.connect(ftpServerIP);
if (ftpClient.login(ftpName, ftpPassword)) {
String remoteFile = "/name/xxx/xxx/xxx文件";
fos = new FileOutputStream("C:/ParseXML_InFTP.xml");
ftpClient.setBufferSize(1024);
if (ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE)) {
ftpClient.retrieveFile(remoteFile, fos);
flag = true;
}
}
} catch (SocketException | IOException e) {
e.printStackTrace();
flag = false;
} finally {
IOUtils.closeQuietly(fos);
disconnectQuietly(ftpClient);
}
return flag;
}
/**
* 建立以日期命名的子目錄
*/
public boolean createDirectory() {
boolean flag = false;
FTPClient ftpClient = new FTPClient();
try {
ftpClient.connect(ftpServerIP);
if (ftpClient.login(ftpName, ftpPassword)) {
if (isValid(ftpPath) && ftpClient.changeWorkingDirectory(ftpPath)) {
String folderName = new SimpleDateFormat("yyyyMMdd").format(new Date()) + "_ReTransmit";
ftpClient.makeDirectory(folderName);
flag = true;
}
}
} catch (SocketException | IOException e) {
e.printStackTrace();
flag = false;
} finally {
disconnectQuietly(ftpClient);
}
return flag;
}
/**
* 取得超過兩天的目錄名稱陣列
*/
public String[] getAllFolderNames() {
FTPClient ftpClient = new FTPClient();
try {
ftpClient.connect(ftpServerIP);
if (ftpClient.login(ftpName, ftpPassword)) {
if (isValid(ftpPath) && ftpClient.changeWorkingDirectory(ftpPath)) {
String threshold = minusTime();
String[] allNames = ftpClient.listNames();
String[] result = new String[allNames.length];
for (int i = 0; i < allNames.length; i++) {
if (allNames[i].substring(0, 8).compareTo(threshold) <= 0) {
result[i] = allNames[i];
}
}
return result;
}
}
} catch (SocketException | IOException e) {
e.printStackTrace();
} finally {
disconnectQuietly(ftpClient);
}
return null;
}
private String minusTime() {
return new SimpleDateFormat("yyyyMMdd")
.format(new Date(System.currentTimeMillis() - 2L * 24 * 60 * 60 * 1000));
}
private boolean isValid(String str) {
return str != null && !str.isEmpty();
}
private void disconnectQuietly(FTPClient client) {
try {
if (client != null && client.isConnected()) {
client.disconnect();
}
} catch (IOException ignored) {}
}
public static void main(String[] args) {
FTPFileTransmit transmit = new FTPFileTransmit();
// 測試清理過期目錄
String[] expired = transmit.getAllFolderNames();
if (expired != null) {
for (String dir : expired) {
if (dir != null && !dir.isEmpty()) {
System.out.println("待清理目錄: " + dir);
}
}
}
}
}
PoolingFTPClientConnectionFactory 管理連線池,避免頻繁建立/銷毀連線開銷。ftpClient.enterLocalPassiveMode()。ftpClient.setControlEncoding("UTF-8"),確保路徑正確解析。RetryPolicy(如 ExponentialBackoff)實作自動重試。FTPSClient)或 SFTP(JSch/SSHJ),避免明文傳輸帳密。Apache Commons Net 提供了成熟、輕量的 FTP 客戶端實作。透過封裝通用操作流程、統一資源釋放與錯誤處理,可快速構建穩健的檔案傳輸模組。實際專案中建議進一步抽象介面,支援多種儲存後端(FTP、SFTP、S3、MinIO 等),實現儲存層解耦。