FTP(File Transfer Protocol)是企業級應用中常見的檔案傳輸協定。Apache Commons Net 的 FTPClient 提供了完整的 FTP 客戶端功能。本文將通過完整代碼範例,展示如何實現檔案上傳、下載,並處理常見坑點。
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.9.0</version> <!-- 請使用最新版本 -->
</dependency>
public boolean uploadFile(String host, int port, String username, String password,
String remotePath, String fileName, InputStream input) {
FTPClient ftp = new FTPClient();
try {
// 1. 連線
ftp.connect(host, port);
if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
ftp.disconnect();
return false;
}
// 2. 登入
ftp.login(username, password);
// 3. 切換工作目錄(不存在需先建立)
ftp.changeWorkingDirectory(remotePath);
// 4. 設定傳輸模式:二進位模式(預設 ASCII 會損壞二進位檔)
ftp.setFileType(FTP.BINARY_FILE_TYPE);
// 5. 被動模式(穿透防火牆/NAT 必須)
ftp.enterLocalPassiveMode();
// 6. 上傳
boolean success = ftp.storeFile(fileName, input);
input.close();
ftp.logout();
return success;
} catch (IOException e) {
log.error("FTP 上傳失敗", e);
return false;
} finally {
closeQuietly(ftp);
}
}
private void closeQuietly(FTPClient ftp) {
if (ftp != null && ftp.isConnected()) {
try { ftp.disconnect(); } catch (IOException ignored) {}
}
}
@Test
public void testUploadFromDisk() throws FileNotFoundException {
try (FileInputStream in = new FileInputStream(new File("D:/test.txt"))) {
boolean flag = uploadFile("127.0.0.1", 21, "user", "pass", "/ftp", "test.txt", in);
System.out.println("上傳結果: " + flag);
}
}
@Test
public void testUploadFromString() throws UnsupportedEncodingException {
InputStream input = new ByteArrayInputStream("測試 FTP 內容".getBytes("UTF-8"));
boolean flag = uploadFile("127.0.0.1", 21, "user", "pass", "/ftp", "test.txt", input);
System.out.println("上傳結果: " + flag);
}
public boolean downloadFile(String host, int port, String username, String password,
String remotePath, String fileName, String localPath) {
FTPClient ftp = new FTPClient();
try {
ftp.connect(host, port);
if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
ftp.disconnect();
return false;
}
ftp.login(username, password);
ftp.setFileType(FTP.BINARY_FILE_TYPE);
ftp.enterLocalPassiveMode();
ftp.changeWorkingDirectory(remotePath);
// 列出檔案確認存在
FTPFile[] files = ftp.listFiles();
for (FTPFile file : files) {
if (file.getName().equals(fileName)) {
File localFile = new File(localPath + "/" + fileName);
try (OutputStream os = new FileOutputStream(localFile)) {
return ftp.retrieveFile(fileName, os);
}
}
}
return false; // 找不到檔案
} catch (IOException e) {
log.error("FTP 下載失敗", e);
return false;
} finally {
closeQuietly(ftp);
}
}
PoolingFTPClientConnectionFactory + GenericObjectPool 管理連線池。setRestartOffset() 支援斷點續傳大檔案。FTPSClient 替代 FTPClient,並配置 SSL/TLS 參數。ftp.setControlEncoding("UTF-8")。ftp.setConnectTimeout(5000)、ftp.setDefaultTimeout(30000) 防止掛死。FTPClient 功能強大但細節多。掌握「二進位模式、被動模式、資源釋放」三大核心,能避開 90% 的坑。生產環境建議封裝為工具類,統一異常處理、日誌記錄、連線池管理。