天天看點

Java通過FTPClient實作登陸、檔案上傳、檔案下載下傳以及檔案夾的周遊

最近工作中遇到需要定期從FTP上下載下傳檔案的需求,故整理了一個工具類以供後續使用。

工具類中主要使用了org.apache.commons.net.ftp中的相關類,是以加入如下的Maven依賴:

<!-- https://mvnrepository.com/artifact/commons-net/commons-net -->
<dependency>
    <groupId>commons-net</groupId>
	<artifactId>commons-net</artifactId>
	<version>3.6</version>
</dependency>
           

建立FTP工具類:FTPUtil.java。具體使用方法詳見注釋:

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.log4j.Logger;

import java.io.*;

public class FTPUtil {
    private static Logger logger = Logger.getLogger(FTPUtil.class);

    /**
     * 登陸FTP并擷取FTPClient對象
     *
     * @param host     FTP主機位址
     * @param port     FTP端口
     * @param userName 登入使用者名
     * @param password 登入密碼
     * @return
     */
    public static FTPClient loginFTP(String host, int port, String userName, String password) {
        FTPClient ftpClient = null;
        try {
            ftpClient = new FTPClient();
            // 連接配接FTP伺服器
            ftpClient.connect(host, port);
            // 登陸FTP伺服器
            ftpClient.login(userName, password);
            // 中文支援
            ftpClient.setControlEncoding("UTF-8");
            // 設定檔案類型為二進制(如果從FTP下載下傳或上傳的檔案是壓縮檔案的時候,不進行該設定可能會導緻擷取的壓縮檔案解壓失敗)
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            ftpClient.enterLocalPassiveMode();

            if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
                logger.error("連接配接FTP失敗,使用者名或密碼錯誤。");
                ftpClient.disconnect();
            } else {
                logger.info("FTP連接配接成功!");
            }
        } catch (Exception e) {
            logger.error("登陸FTP失敗,請檢查FTP相關配置資訊是否正确!", e);
        }
        return ftpClient;
    }

    /**
     * 從FTP下載下傳檔案到本地
     * @param ftpClient     已經登陸成功的FTPClient
     * @param ftpFilePath   FTP上的目标檔案路徑
     * @param localFilePath 下載下傳到本地的檔案路徑
     */
    public static void downloadFileFromFTP(FTPClient ftpClient, String ftpFilePath, String localFilePath) {
        InputStream is = null;
        FileOutputStream fos = null;
        try {
            // 擷取ftp上的檔案
            is = ftpClient.retrieveFileStream(ftpFilePath);
            fos = new FileOutputStream(new File(localFilePath));
            // 檔案讀取方式一
            int i;
            byte[] bytes = new byte[1024];
            while ((i = is.read(bytes)) != -1) {
                fos.write(bytes, 0, i);
            }
            // 檔案讀取方式二
            //ftpClient.retrieveFile(ftpFilePath, new FileOutputStream(new File(localFilePath)));
            ftpClient.completePendingCommand();
            logger.info("FTP檔案下載下傳成功!");
        } catch (Exception e) {
            logger.error("FTP檔案下載下傳失敗!", e);
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 上傳本地檔案到FTP
     * @param ftpClient         已經登陸成功的FTPClient
     * @param localFilePath     要上傳的本地檔案路徑
     * @param ftpFilePath       要儲存的FTP檔案路徑
     */
    public static void uploadFileToFTP(FTPClient ftpClient, String localFilePath, String ftpFilePath) {
        OutputStream os = null;
        FileInputStream fis = null;
        try {
            // 擷取ftp上的檔案
            os = ftpClient.storeFileStream(ftpFilePath);
            fis = new FileInputStream(new File(localFilePath));
            // 檔案儲存方式一
            int length;
            byte[] bytes = new byte[1024];
            while ((length = fis.read(bytes)) != -1) {
                os.write(bytes, 0, length);
            }
            // 檔案儲存方式二
            //ftpClient.storeFile(ftpFilePath, new FileInputStream(new File(localFilePath)));
            ftpClient.completePendingCommand();
            logger.info("FTP檔案上傳成功!");
        } catch (Exception e) {
            logger.error("FTP檔案上傳失敗!", e);
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
                if (os != null) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 擷取FTP某一特定目錄下的所有檔案名稱
     * @param ftpClient     已經登陸成功的FTPClient
     * @param ftpDirPath    FTP上的目标檔案路徑
     */
    public void getFileNameListFromFTP(FTPClient ftpClient, String ftpDirPath) {
        try {
            if (ftpDirPath.startsWith("/") && ftpDirPath.endsWith("/")) {
                // 通過提供的檔案路徑擷取FTPFile對象清單
                FTPFile[] files = ftpClient.listFiles(ftpDirPath);
                // 周遊檔案清單,列印出檔案名稱
                for (int i = 0; i < files.length; i++) {
                    FTPFile ftpFile = files[i];
                    // 此處隻列印檔案,未周遊子目錄(如果需要周遊,加上遞歸邏輯即可)
                    if (ftpFile.isFile()) {
                        logger.info(ftpDirPath + ftpFile.getName());
                    }
                }
            } else {
                logger.error("目前FTP路徑不可用");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
           

最後建立測試類FTPUtilTest.java:

import org.apache.commons.net.ftp.FTPClient;

import java.io.IOException;

public class FTPUtilTest {
    public static void main(String[] args) throws IOException {
        FTPClient ftpClient = FTPUtil.loginFTP("192.168.8.88", 21, "userName", "******");
        FTPUtil.downloadFileFromFTP(ftpClient, "/usr/local/test.txt", "/Users/123/Downloads/test.txt");
        FTPUtil.uploadFileToFTP(ftpClient, "/Users/123/Downloads/test2.txt", "/usr/local/test2.txt");
        ftpClient.disconnect();
    }
}
           

注意事項:

一、很多人在循環調用上傳或下載下傳方法的時候,有的會遇到第一次調用沒問題,但是從第二次開始ftpClient.retrieveFileStream和ftpClient.storeFileStream傳回null的問題,是以此問題的原因在于漏掉了下面這句代碼

ftpClient.completePendingCommand();
           

切記在成功讀取或者上傳一個檔案之後調用該方法。

二、如果遇到上傳或者下載下傳後的Zip檔案無法打開或者使用密碼無法解壓的情況,則是遺漏了下面這句代碼:

ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);