首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java FTP 文件下載完整實現:基於 Apache Commons Net 的生產級代碼

     編輯:Java 相關知識

技術選型

採用 Apache Commons Net 库(org.apache.commons.net.ftp.FTPClient),為 Java FTP 操作的標準選擇,功能完整、線程安全、廣泛用於生產環境。

Maven 依賴

<dependency>
    <groupId>commons-net</groupId>
    <artifactId>commons-net</artifactId>
    <version>3.9.0</version> <!-- 或更新版本 -->
</dependency>

核心下載邏輯

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPClientConfig;
import java.io.*;

public class FtpDownloader {
    private static final int DEFAULT_PORT = 21;
    private static final int TIMEOUT_MS = 120_000; // 2分鐘
    private static final int BUFFER_SIZE = 1024;

    public void downloadFiles(String host, String user, String password, String remoteDir, String localDir) {
        FTPClient ftpClient = new FTPClient();
        try {
            // 1. 連接配置
            ftpClient.setDefaultPort(DEFAULT_PORT);
            ftpClient.setConnectTimeout(TIMEOUT_MS);
            ftpClient.setDefaultTimeout(TIMEOUT_MS);
            ftpClient.setDataTimeout(TIMEOUT_MS);
            ftpClient.setControlEncoding("UTF-8"); // 關鍵:中文文件名支持

            // 2. 連接並登錄
            ftpClient.connect(host);
            if (!ftpClient.login(user, password)) {
                throw new IOException("FTP 登錄失敗: " + ftpClient.getReplyString());
            }

            // 3. 設置被動模式(穿透防火牆/NAT)
            ftpClient.enterLocalPassiveMode();

            // 4. 切換遠程目錄
            if (!ftpClient.changeWorkingDirectory(remoteDir)) {
                throw new IOException("無法切換到遠程目錄: " + remoteDir);
            }

            // 5. 列出文件並下載
            FTPFile[] files = ftpClient.listFiles();
            if (files == null || files.length == 0) {
                System.out.println("遠程目錄無文件");
                return;
            }

            for (FTPFile file : files) {
                if (!file.isFile()) continue; // 跳過目錄

                String remoteFileName = file.getName();
                File localFile = new File(localDir, remoteFileName);
                localFile.getParentFile().mkdirs();

                // 6. 設置二進制傳輸類型
                ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
                ftpClient.setBufferSize(BUFFER_SIZE);

                try (InputStream in = ftpClient.retrieveFileStream(remoteFileName);
                     FileOutputStream out = new FileOutputStream(localFile)) {

                    if (in == null) {
                        System.err.println("無法獲取文件流: " + remoteFileName);
                        continue;
                    }

                    byte[] buffer = new byte[BUFFER_SIZE];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) != -1) {
                        out.write(buffer, 0, bytesRead);
                    }
                    out.flush();
                }

                // 7. 完成待處理命令(必須調用,否則連接卡死)
                if (!ftpClient.completePendingCommand()) {
                    System.err.println("文件傳輸確認失敗: " + remoteFileName);
                } else {
                    System.out.println("下載成功: " + remoteFileName);
                    // 可選:下載後刪除遠程文件
                    // ftpClient.deleteFile(remoteFileName);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException("FTP 下載異常", e);
        } finally {
            // 8. 安全斷開連接
            try {
                if (ftpClient.isConnected()) {
                    ftpClient.logout();
                    ftpClient.disconnect();
                }
            } catch (IOException ignored) {}
        }
    }
}

關鍵最佳實踐

  1. UTF-8 編碼:setControlEncoding("UTF-8") 解決中文文件名亂碼。
  2. 被動模式:enterLocalPassiveMode() 適應多數防火牆/NAT 環境。
  3. 二進制模式:setFileType(BINARY_FILE_TYPE) 避免文本模式損壞二進制文件。
  4. completePendingCommand():必須調用,FTP 協議要求數據傳輸後確認命令完成,否則連接會卡住。
  5. 超時設置:連接、數據、控制三類超時全部設置,防止掛死。
  6. 資源管理:使用 try-with-resources 確保流關閉,finally 塊確保 FTP 連接斷開。

斷點續傳擴展(大文件必備)

// 設置斷點續傳起始位置
long localSize = localFile.length();
if (localSize > 0) {
    ftpClient.setRestartOffset(localSize); // 告知服務器從該偏移量開始傳輸
    try (FileOutputStream out = new FileOutputStream(localFile, true); // 追加模式
         InputStream in = ftpClient.retrieveFileStream(remoteFileName)) {
        // 寫入邏輯同前
    }
}

常見問題排查

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
現象原因解決
連接超時防火牆阻擋、IP 錯誤、端口非 21telnet 測試連通性、確認端口、設置被動模式
中文文件名亂碼/404編碼不一致setControlEncoding("UTF-8")、服務器也需支持 UTF-8
下載大文件卡死未調用 completePendingCommand()每次 retrieveFileStream 後必須調用
空文件/損壞ASCII 模式傳輸二進制文件強制 BINARY_FILE_TYPE