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

Java 使用 Apache Commons Net 實作 FTP 檔案上傳與下載完整範例

     編輯:Java 程式設計入門

前言

在企業級應用開發中,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);
                }
            }
        }
    }
}

最佳實踐建議

  1. 連線池化:高併發場景下建議使用 PoolingFTPClientConnectionFactory 管理連線池,避免頻繁建立/銷毀連線開銷。
  2. 被動模式:跨防火牆/NAT 環境需啟用 ftpClient.enterLocalPassiveMode()。
  3. 編碼處理:中文檔名需設定 ftpClient.setControlEncoding("UTF-8"),確保路徑正確解析。
  4. 異常重試:網路抖動常見,可引入 RetryPolicy(如 ExponentialBackoff)實作自動重試。
  5. 安全性:生產環境請改用 FTPS(FTPSClient)或 SFTP(JSch/SSHJ),避免明文傳輸帳密。

總結

Apache Commons Net 提供了成熟、輕量的 FTP 客戶端實作。透過封裝通用操作流程、統一資源釋放與錯誤處理,可快速構建穩健的檔案傳輸模組。實際專案中建議進一步抽象介面,支援多種儲存後端(FTP、SFTP、S3、MinIO 等),實現儲存層解耦。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved