1. 程式人生 > >使用commons-net來下載文件(僅做記錄)

使用commons-net來下載文件(僅做記錄)

datatime repl eve time ole pri 讀取數據 mod ets

使用 commons-net-3.6.jar

public class ApacheFtpUtil {
    private Logger log = LoggerFactory.getLogger(ApacheFtpUtil.class);
    private String host;
    private String username;
    private String password;
    // 控制端口
    private int port = 21;

    // 秒數
    private int defaultTimeoutSecond = 60;
    private int connectTimeoutSecond = 30;
    private int dataTimeoutSecond = 30;
    // 秒
    private int controlKeepAliveTimeout = 120;

    private boolean isTextMode = false;

    private String tranEncoding = "GB2312";

    private FTPClient client;

    public ApacheFtpUtil(String host, String username, String password) {
        this(host, username, password, 21, 0);
    }

    public ApacheFtpUtil(String host, String username, String password, int port) {
        this(host, username, password, port, 0);
    }

    public ApacheFtpUtil(String host, String username, String password, int port, int security) {
        this.host = host;
        this.port = port;
        this.username = username;
        this.password = password;
    }

    public void initClint() throws Exception {
        // FTPSClient
        client = new FTPClient();

        // 每大約2分鐘發一次noop,防止大文件傳輸導致的控制連接中斷
        client.setControlKeepAliveTimeout(controlKeepAliveTimeout);
        // client.setListHiddenFiles(listHiddenFiles);
        // FTPClientConfig config = new
        // FTPClientConfig(FTPClientConfig.SYST_NT);
        // config.setServerLanguageCode("zh");
        // config.setUnparseableEntries(saveUnparseable);
        // config.setDefaultDateFormatStr(defaultDateFormat);
        // config.setRecentDateFormatStr(recentDateFormat);
        // client.configure(config);

        // 設置默認超時
        client.setDefaultTimeout(defaultTimeoutSecond * 1000);
        // 連接超時
        // client.setConnectTimeout(connectTimeoutSecond * 1000);
        // 設置數據超時
        // client.setDataTimeout(dataTimeoutSecond * 1000);
        /*
         * socket超時 設置一個命令執行後最大等待Server反饋的時間. 如果對方連接狀態60秒沒有收到數據的話強制斷開客戶端
         */
        // client.setSoTimeout(60 * 1000);
        // client.setBufferSize(1024);
        // 連接到遠程的FTP服務器.
        try {
            client.connect(this.host, this.port);
        } catch (Exception e) {
            this.destory();
            log.error("連接服務器失敗,host:{}, port:{}", this.host, this.port);
            throw e;
        }
        int reply = client.getReplyCode(); // 獲得返回的代碼,來判斷連接狀態
        if (!FTPReply.isPositiveCompletion(reply)) {
            this.destory();
            // 連接錯誤
            throw new Exception("Can‘t Connect to :" + host);
        }
        // 登錄
        boolean flag = client.login(this.username, this.password);
        if (flag == false) {
            this.destory();
            throw new Exception("Invalid user/password");
        }
        try {
            if (FTPReply.isPositiveCompletion(client.sendCommand("OPTS UTF8", "ON"))) {
                // 開啟服務器對UTF-8的支持,如果服務器支持就用UTF-8編碼,否則就使用本地編碼
                tranEncoding = "UTF-8";
                log.debug("FTP Server allow UTF8 encoding");
            }
        } catch (Exception e) {
            log.error("ftp server opts utf8 error,{}", e.getMessage());
        }
        log.info("use encoding {}", tranEncoding);
        client.setControlEncoding(tranEncoding); // 中文支持
        // 設置傳送模式
        if (isTextMode) {
            client.setFileType(FTP.ASCII_FILE_TYPE);
        } else {
            client.setFileType(FTP.BINARY_FILE_TYPE);
        }
        /*
         * PORT中文稱為主動模式,工作的原理:
         * FTP客戶端連接到FTP服務器的21端口,發送用戶名和密碼登錄,登錄成功後要list列表或者讀取數據時,
         * 客戶端隨機開放一個端口(1024以上),發送 PORT命令到FTP服務器,告訴服務器客戶端采用主動模式並開放端口;
         * FTP服務器收到PORT主動模式命令和端口號後,通過服務器的20端口和客戶端開放的端口連接,發送數據.
         * 主動模式需要客戶端必須開放端口給服務器,很多客戶端都是在防火墻內,開放端口給FTP服務器訪問比較困難。
         */
        // client.enterLocalActiveMode(); //主動模式
        /*
         * PASV是Passive的縮寫,中文成為被動模式.
         * 工作原理:FTP客戶端連接到FTP服務器的21端口,發送用戶名和密碼登錄,登錄成功後要list列表或者讀取數據時,
         * 發送PASV命令到FTP服務器, 服務器在本地隨機開放一個端口(1024以上),然後把開放的端口告訴客戶端,
         * 客戶端再連接到服務器開放的端口進行數據傳輸. 被動模式只需要服務器端開放端口給客戶端連接就行了。
         */
        client.enterLocalPassiveMode();
        // client.setUseEPSVwithIPv4(useEpsvWithIPv4);
        log.debug("==========初始化FTP連接成功===========");
    }

    public void destory() {
        if (client == null) {
            return;
        }
        try {
            client.logout();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            client.disconnect();
        } catch (IOException e) {
            e.printStackTrace();
        }
        client = null;
    }

    public void changeRemoteDir(String newPath) throws RuntimeException {
        // 切換路徑[相對路徑和絕對路徑都可]
        if (StringUtils.isBlank(newPath)) {
            return;
        }
        try {
            boolean flag = client.changeWorkingDirectory(new String(newPath.getBytes(tranEncoding), "ISO-8859-1"));
            log.debug("change working directory to {}, status:{}", newPath, flag);
        } catch (IOException e) {
            throw new RuntimeException("切換路徑失敗:" + newPath + "; " + e.getMessage());
        }
    }

    /**
     * 下載文件
     */
    public void download(String remoteFile, String localOutFile) throws RuntimeException {
        FileOutputStream outstream = null;
        // 下載文件
        try {
            log.debug("開始下載文件 {}", remoteFile);
            outstream = new FileOutputStream(new File(localOutFile));
            /*
             * 這個方法的意思就是每次數據連接之前,ftp client告訴ftp server開通一個端口來傳輸數據. 因為ftp
             * server可能每次開啟不同的端口來傳輸數據,但是在linux上,由於安全限制,可能某些端口沒有開啟,所以就出現阻塞
             */
            client.enterLocalPassiveMode();
            client.setFileType(FTP.BINARY_FILE_TYPE);
            /*
             * FTP協議裏面,規定文件名編碼為iso-8859-1,所以目錄名或文件名需要轉碼 retrieveFile的第一個參數需要是
             * ISO-8859-1 編碼
             */
            client.retrieveFile(new String(remoteFile.getBytes(tranEncoding), "ISO-8859-1"), outstream);
        } catch (IOException e) {
            throw new RuntimeException("下載文件失敗:" + remoteFile + "; " + e.getMessage());
        } finally {
            if (outstream != null) {
                try {
                    outstream.flush();
                    outstream.close();
                } catch (IOException e) {
                }
            }
        }
        log.debug("下載文件 {} 完成", remoteFile);
    }
}

使用commons-net來下載文件(僅做記錄)