java連接ftp上傳、下載、刪除、查詢工具類

簡介

  1. 當前ftp工具類使用java連接,可進行上傳、下載、查詢、刪除(文件或文件夾),需提前在服務器搭建好ftp環境;
  2. linux搭建ftp環境站點請查看上一篇文章:Linux實例搭建FTP站點

實例

package com.sixmonth.app.platform.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * -FTP工具類(common-net:ftp) 
 *     -注意:
 *         1.-ftpClient.changeWorkingDirectory(ftpPath),切換ftp目錄會產生權限問題,需謹慎使用
 *         2.-ftpPath可以寫相對路徑或絕對路徑,一般填寫絕對路徑,以防止工作目錄的切換產生權限問題;如果是相對路徑,一般需要切換回根目錄
 * @author sixmonth
 * @Date 2019年12月19日
 *
 */
public class FtpUtils {

    private static Logger logger = LoggerFactory.getLogger(FtpUtils.class);

    // ftp服務器地址
    public String hostname = "47.111.111.111";
    // ftp服務器端口號
    public Integer port = 21;
    // ftp登錄賬號
    public String username = "test";
    // ftp登錄密碼
    public String password = "123456";
    // ftp根目錄(自定義,即vsftpd.conf屬性配置,ep:local_root=/var/ftp/test)
    public static String rootPath = "/var/ftp/test/";

    /**
     * --初始化ftp服務器
     */
    public FTPClient initFtpClient() {
        FTPClient ftpClient = new FTPClient();
        ftpClient.setControlEncoding("utf-8");
        try {
            logger.info("連接ftp服務器:" + this.hostname + ":" + this.port);
            ftpClient.connect(hostname, port); // 連接ftp服務器
            ftpClient.login(username, password); // 登錄ftp服務器
            int replyCode = ftpClient.getReplyCode(); // 是否成功登錄服務器
            if (!FTPReply.isPositiveCompletion(replyCode)) {
                logger.info("ftp連接失敗!");
            } else {
                logger.info("ftp連接成功");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ftpClient;
    }

    /**
     * -工作目錄切換回根目錄
     * 
     * @param ftpClient
     * @throws IOException
     */
    public void changeRootDirectory(FTPClient ftpClient) throws IOException {
        ftpClient.changeWorkingDirectory(rootPath);
    }

    /**
     * -上傳文件
     * @param ftpClient
     * @param ftpPath
     *            -ftp服務保存地址(歸屬多層文件夾) -注意:
     *            1.-當前ftpPath屬於多層文件夾,一般寫法爲:sixmonth/day1;sixmonth/day2
     *            2.-也可以寫成服務器的絕對路徑,絕對路徑則屬於單層文件夾,寫法爲:/var/ftp/test
     * @param newFileName
     *            -上傳到ftp的新文件名,有無後綴都行
     * @param originfile
     *            -待上傳文件(文件絕對地址)
     * @return
     */
    public boolean uploadFile(FTPClient ftpClient, String ftpPath, String newFileName, String originfile) {
        InputStream inputStream = null;
        try {
            logger.info("開始上傳文件");
            changeRootDirectory(ftpClient);// 切換根目錄
            inputStream = new FileInputStream(new File(originfile));
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            ftpClient.setControlEncoding("UTF-8");

            CreateDirecroty(ftpClient, ftpPath);
            ftpClient.makeDirectory(ftpPath);

            // 切換ftp工作目錄,會產生權限問題,需謹慎使用
            ftpClient.changeWorkingDirectory(ftpPath);

            // 每次數據連接之前,ftp client告訴ftp server開通一個端口來傳輸數據
            ftpClient.enterLocalPassiveMode();
            // 設置傳輸模式
            ftpClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);

            String temName = newFileName;
            if (newFileName.indexOf(".") == -1) {
                temName = newFileName + "." + Tool.getPrefix(originfile);
            }

            // 觀察是否真的上傳成功
            boolean storeFlag = ftpClient.storeFile(temName, inputStream);
            // boolean storeFlag = ftpClient.storeFile(new
            // String(fileName.getBytes("UTF-8"), "ISO-8859-1"), inputStream);
            System.err.println("storeFlag==" + storeFlag);
            inputStream.close();
            logger.info("上傳文件成功");
        } catch (Exception e) {
            logger.info("上傳文件失敗");
            e.printStackTrace();
        } finally {
            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return true;
    }

    /**
     * -上傳文件
     * @param ftpClient
     * @param ftpPath
     *            -ftp服務保存地址(歸屬多層文件夾) -注意:
     *            1.-當前ftpPath屬於多層文件夾,一般寫法爲:sixmonth/day1;sixmonth/day2
     *            2.-也可以寫成服務器的絕對路徑,絕對路徑則屬於單層文件夾,寫法爲:/var/ftp/test
     * @param newFileName
     *            -上傳到ftp的文件名,必須有後綴,ep:test.txt
     * @param inputStream
     *            -輸入文件流
     * @return
     */
    public boolean uploadFile(FTPClient ftpClient, String ftpPath, String newFileName, InputStream inputStream) {
        try {
            logger.info("開始上傳文件");
            changeRootDirectory(ftpClient);// 切換根目錄
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            CreateDirecroty(ftpClient, ftpPath);
            ftpClient.makeDirectory(ftpPath);
            // 切換ftp工作目錄,會產生權限問題,需謹慎使用
            ftpClient.changeWorkingDirectory(ftpPath);
            ftpClient.storeFile(newFileName, inputStream);
            inputStream.close();
            logger.info("上傳文件成功");
        } catch (Exception e) {
            logger.error("上傳文件失敗");
            e.printStackTrace();
        } finally {
            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return true;
    }

    /**
     * -切換ftp目錄
     * @param ftpClient
     * @param directory -ftp路徑
     * @return
     */
    public boolean changeWorkingDirectory(FTPClient ftpClient, String directory) {
        boolean flag = true;
        try {
            flag = ftpClient.changeWorkingDirectory(directory);
            if (flag) {
                logger.info("進入文件夾" + directory + " 成功!");
            } else {
                logger.info("進入文件夾" + directory + " 失敗!開始創建文件夾");
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
        return flag;
    }

    /**
     * -創建多層目錄文件夾,如果有ftp服務器已存在該文件,則不創建,如果無,則創建
     * @param ftpClient
     * @param remoteDir -ftp文件夾
     * @return
     * @throws IOException
     */
    public boolean CreateDirecroty(FTPClient ftpClient, String remoteDir) throws IOException {
        boolean success = true;
        String directory = remoteDir + "/";
        // 如果遠程目錄不存在,則遞歸創建遠程服務器目錄
        if (!directory.equalsIgnoreCase("/") && !changeWorkingDirectory(ftpClient, new String(directory))) {
            int start = 0;
            int end = 0;
            if (directory.startsWith("/")) {
                start = 1;
            } else {
                start = 0;
            }
            end = directory.indexOf("/", start);
            String path = "";
            String paths = "";
            while (true) {
                String subDirectory = new String(remoteDir.substring(start, end).getBytes("GBK"), "iso-8859-1");
                path = path + "/" + subDirectory;
                if (!existFile(ftpClient, path)) {
                    if (makeDirectory(ftpClient, subDirectory)) {
                        changeWorkingDirectory(ftpClient, subDirectory);
                    } else {
                        logger.info("創建目錄[" + subDirectory + "]失敗");
                        changeWorkingDirectory(ftpClient, subDirectory);
                    }
                } else {
                    changeWorkingDirectory(ftpClient, subDirectory);
                }

                paths = paths + "/" + subDirectory;
                start = end + 1;
                end = directory.indexOf("/", start);
                // 檢查所有目錄是否創建完畢
                if (end <= start) {
                    break;
                }
            }
        }
        return success;
    }

    /**
     * -判斷ftp服務器文件是否存在
     * @param ftpClient
     * @param path -文件相對路徑或絕對路徑
     * @return
     * @throws IOException
     */
    public boolean existFile(FTPClient ftpClient, String path) throws IOException {
        boolean flag = false;
        FTPFile[] ftpFileArr = ftpClient.listFiles(path);
        if (ftpFileArr.length > 0) {
            flag = true;
        }
        return flag;
    }

    /**
     * -創建ftp目錄
     * @param ftpClient
     * @param dir
     * @return
     */
    public boolean makeDirectory(FTPClient ftpClient, String dir) {
        boolean flag = true;
        try {
            flag = ftpClient.makeDirectory(dir);
            if (flag) {
                logger.info("創建文件夾" + dir + " 成功!");
            } else {
                logger.info("創建文件夾" + dir + " 失敗!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return flag;
    }

    /**
     * - 下載文件
     * @param ftpClient
     * @param ftpPath
     *            -FTP服務器文件目錄
     * @param filename
     *            -文件名稱
     * @param localpath
     *            -下載後的文件路徑
     * @return
     */
    public boolean downloadFile(FTPClient ftpClient, String ftpPath, String filename, String localpath) {
        boolean flag = false;
        OutputStream os = null;
        try {
            logger.info("開始下載文件");

            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);// 傳輸的時候以二進制傳輸
            ftpClient.enterLocalPassiveMode();
            // ftpClient.setRemoteVerificationEnabled(false);

            // ftpClient.configure(new
            // FTPClientConfig("com.zznode.tnms.ra.c11n.nj.resource.ftp.UnixFTPEntryParser"));
            // 由於apache不支持中文語言環境,通過定製類解析中文日期類型
            ftpClient.configure(new FTPClientConfig("com.zznode.tnms.ra.c11n.nj.resource.ftp.UnixFTPEntryParser"));

            //切換FTP目錄
            ftpClient.changeWorkingDirectory(ftpPath);

            // 查看有哪些文件夾 以確定切換的ftp路徑正確
            String[] a = ftpClient.listNames();
            logger.info(a[0]);

            FTPFile[] ftpFiles = ftpClient.listFiles();
            for (FTPFile file : ftpFiles) {
                if (filename.equalsIgnoreCase(file.getName())) {
                    File localFile = new File(localpath + "/" + file.getName());
                    os = new FileOutputStream(localFile);
                    ftpClient.retrieveFile(file.getName(), os);
                    os.close();
                }
            }
            flag = true;
            logger.info("下載文件成功");
        } catch (Exception e) {
            logger.info("下載文件失敗");
            e.printStackTrace();
        } finally {
            if (null != os) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return flag;
    }

    /**
     * -遞歸遍歷出目錄下面所有文件
     * @param ftpClient
     * @param ftpPath
     *            ftp相對路徑或絕對路徑
     * @param filePathsArr
     *            需要返回的文件路徑集合
     * @return
     * @throws IOException
     */
    public List<String> listFiles(FTPClient ftpClient, String ftpPath, List<String> filePathsArr) throws IOException {
        FTPFile[] files = ftpClient.listFiles(ftpPath);
        for (FTPFile file : files) {
            String path = ftpPath + "/" + file.getName();
            if (file.isFile()) {
                filePathsArr.add(path);
            } else if (file.isDirectory()) {
                //過濾當前目錄"."和上一級目錄"..",防止死循環
                if (!".".equals(file.getName()) && !"..".equals(file.getName())) {
                    listFiles(ftpClient, path, filePathsArr);
                }
            }
        }
        return filePathsArr;
    }

    /**
     * -刪除單個文件
     * @param ftpClient
     * @param ftpPath
     *            -FTP服務器保存目錄
     * @param filename
     *            -要刪除的文件名稱
     * @return
     */
    public boolean deleteFile(FTPClient ftpClient, String ftpPath, String filename) {
        boolean flag = false;
        try {
            logger.info("開始刪除文件");
            // 切換FTP目錄(切換ftp目錄會產生權限問題,則不可與遍歷刪除並用)
            // ftpClient.changeWorkingDirectory(ftpPath);
            flag = ftpClient.deleteFile(ftpPath + "/" + filename);
            if (flag) {
                logger.info("刪除文件成功");
            } else {
                logger.info("刪除文件失敗");
            }
        } catch (Exception e) {
            logger.info("刪除文件失敗");
            e.printStackTrace();
        }
        return flag;
    }

    /**
     * -刪除文件夾內的所有文件以及空文件夾 
     *     -注意: 
     *         1.-該方法會刪除文件夾內的所有遍歷遞歸文件以及空文件夾
     *         2.-該方法需要疊加效果(第一次刪除所有文件,第二次刪除文件夾),第二次可等待下一次執行該方法
     *         3.-ftpClient.removeDirectory(ftpPath)的方法對非空文件夾無效
     *         4.-ftpClient.changeWorkingDirectory(ftpPath),切換ftp目錄會產生權限問題,需謹慎使用
     * @param ftpClient
     * @param ftpPath
     *            -ftp路徑
     * @return true 表似成功,false 失敗
     */
    public boolean deleteDir(FTPClient ftpClient, String ftpPath) {
        boolean flag = false;
        try {
            flag = iterateDelete(ftpClient, ftpPath);
            logger.info("ftp文件刪除成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return flag;
    }

    /**
     * -遍歷刪除文件以及空文件夾
     * @param ftpClient
     * @param ftpPath
     * @return true 表似成功,false 失敗
     * @throws IOException
     */
    public boolean iterateDelete(FTPClient ftpClient, String ftpPath) throws IOException {
        FTPFile[] files = ftpClient.listFiles(ftpPath);
        boolean flag = false;
        for (FTPFile f : files) {
            String path = ftpPath + "/" + f.getName();
            FTPFile[] filesTem = ftpClient.listFiles(path);
            if (f.isDirectory() && filesTem.length == 0) {//空文件夾不再進入遞歸
                boolean bool = ftpClient.removeDirectory(path);
                logger.info("刪除空文件夾:" + bool + " " + path);
            } else if (f.isDirectory() && filesTem.length > 0) {//非空文件夾進入遞歸
                iterateDelete(ftpClient, path);
            } else {//文件直接刪除
                deleteFile(ftpClient, ftpPath, f.getName());
            }
        }
        //刪除空文件夾
        if (files.length == 0) {
            flag = ftpClient.removeDirectory(ftpPath);
        } else {
            flag = false;
        }
        return flag;
    }

    public static void main(String[] args) {
        FtpUtils ftp = new FtpUtils();
        FTPClient ftpClient = ftp.initFtpClient();
        try {
            /**
             * -上傳文件
             */
            // 使用流上傳
            // FileInputStream inputStream = new FileInputStream(new File("D:\\sixmonth\\test.txt"));
            // ftp.uploadFile(ftpClient,"sixmonth", "test.txt", inputStream);
            // 使用文件上傳,同時測試多層目錄
            // ftp.uploadFile(ftpClient, "sixmonth/test", "test.txt", "D:\\sixmonth\\test.txt");
            // ftp.uploadFile(ftpClient,"sixmonth/test3", "test.txt", "D:\\sixmonth\\test.txt");
            
            /**
             * -下載文件
             */
            // ftp.downloadFile("sixmonth/test", "test.txt", "D://");

            /**
             * -刪除文件
             */
            // ftp.deleteFile(ftpClient,"sixmonth/test", "test.txt");//刪除單個文件
            // ftp.deleteDir(ftpClient,"sixmonth/test");//刪除文件夾

            /**
             * -遍歷ftp所有文件夾,返回文件路徑集合
             */
             List<String> filePathsArr = new ArrayList<String>();
             ftp.listFiles(ftpClient, "sixmonth", filePathsArr);
             logger.info(filePathsArr.toString());

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (ftpClient != null) {
                try {
                    ftpClient.logout();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
                if (ftpClient.isConnected()) {
                    try {
                        ftpClient.disconnect();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

    }
}

注意事項

  1. ftpClient.changeWorkingDirectory(ftpPath),切換ftp目錄會產生權限問題,需謹慎使用;
  2. ftpPath可以寫相對路徑或絕對路徑,一般填寫絕對路徑,以防止工作目錄的切換產生權限問題;如果是相對路徑,多個操作同時進行的話,一般需要切換回根目錄
  3. ftpClient.removeDirectory方法只能刪除ftp上的空文件夾,對非空文件夾是無效的的,如果需要刪除文件夾,必須先遍歷刪除所有的文件,然後再刪除空文件夾;

總結

實踐是檢驗認識真理性的唯一標準,自己動手,豐衣足食!

 

發佈了78 篇原創文章 · 獲贊 272 · 訪問量 54萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章