java操作ftp server

stono發表於2024-04-24

參考:https://www.jianshu.com/p/2efc2669b736

POM依賴

        <dependency>
            <groupId>commons-net</groupId>
            <artifactId>commons-net</artifactId>
            <version>3.6</version>
        </dependency>

配置資訊:

ftp:
  hostName: 192.168.0.2
  port: 21
  userName:root
  passWord: root
  defaultLocalPath: D:/ftp  //下載到本地儲存的路徑
  defaultRemotePath: /ftp/ //ftp server上的檔案路徑

FTP建立:

@Component
public class FtpClientFactory {

    private static final String ENCODING = "utf-8";

    private static final int TIMEOUT = 10 * 1000;

    private final FtpConfig ftpConfig;

    public FtpClientFactory(FtpConfig ftpConfig) {
        this.ftpConfig = ftpConfig;
    }

    public FTPClient makeClient() {
        FTPClient ftpClient = new FTPClient();
        ftpClient.setConnectTimeout(TIMEOUT);
        ftpClient.setControlEncoding(ENCODING);
        try {
            // connect ftp server
            ftpClient.connect(ftpConfig.getHostName(), ftpConfig.getPort());
            // login ftp server
            ftpClient.login(ftpConfig.getUserName(), ftpConfig.getPassWord());
            // check whether login in to the server
            int replyCode = ftpClient.getReplyCode();
            if (FTPReply.isPositiveCompletion(replyCode)) {
                log.info("The login to the FTP server is successful.");
            } else {
                log.error("The login to the FTP server failed.");
                return null;
            }
            // File Type
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            // Passive Mode
            ftpClient.enterLocalActiveMode();
        } catch (Exception e) {
            log.error("factory make client exception", e);
            destroyClient(ftpClient);
        }
        return ftpClient;
    }

    public void destroyClient(FTPClient ftpClient) {
        try {
            if (ftpClient != null && ftpClient.isConnected()) {
                ftpClient.logout();
            }
        } catch (Exception e) {
            log.error("ftpClient logout exception", e);
        } finally {
            try {
                if (ftpClient != null) {
                    ftpClient.disconnect();
                }
            } catch (Exception e2) {
                log.error("ftpClient disconnect exception", e2);
            }
        }
    }

    public boolean validateClient(FTPClient ftpClient) {
        try {
            return ftpClient.sendNoOp();
        } catch (Exception e) {
            log.error("ftpClient validate exception", e);
        }
        return false;
    }

FTP工具類:

public class FtpUtils {

    private static final String SLASH_STR = "/";

    private static final String NOT_FIND_DIRECTORY = "Can not find the directory in ftp server.";

    private static final String NOT_FIND_FILE = "Can not find the file in the path.";

    private static final String NOT_FIND_LOCAL_PATH = "Can not find the local path.";

    private static final String LOCAL_PATH_NOT_DIRECTORY = "The local path is not a directory.";

    /**
     * traverse all files under the directory recursively
     *
     * @param ftpClient ftp client
     * @param fileList  file list
     * @param pathName  directory to be traversed, must start and end with a slash(/)
     */
    public static List<String> listFile(FTPClient ftpClient, List<String> fileList, String pathName) {
        if (pathName.startsWith(SLASH_STR) && pathName.endsWith(SLASH_STR)) {
            //change working directory
            changeWorkingDirectory(ftpClient, pathName);
            FTPFile[] files = new FTPFile[0];
            try {
                files = ftpClient.listFiles();
            } catch (IOException e) {
                log.error("Get ftp server file list error.");
            }
            for (FTPFile file : files) {
                if (file.isFile()) {
                    fileList.add(pathName + file.getName());
                } else if (file.isDirectory()) {
                    /*
                     * This judgment needs to be added.
                     * FTP will include the directory under the project file directory(./)
                     * and the directory under the directory one level up from the project
                     * file directory(../) in the recursion.
                     * It will cause a cycle, so need to filter it out.
                     */
                    if (!".".equals(file.getName()) && !"..".equals(file.getName())) {
                        listFile(ftpClient, fileList, pathName + file.getName() + SLASH_STR);
                    }
                }
            }
        }
        return fileList;
    }

    /**
     * traverse the specified type file under the directory recursively
     *
     * @param ftpClient ftp client
     * @param fileList  file list
     * @param pathName  directory to be traversed, must start and end with a slash(/)
     * @param ext       the extension of the file
     */
    public static List<String> listFileByExt(FTPClient ftpClient, List<String> fileList, String pathName, String ext) {
        if (pathName.startsWith(SLASH_STR) && pathName.endsWith(SLASH_STR)) {
            FTPFile[] files = new FTPFile[0];
            try {
                //change working directory
                changeWorkingDirectory(ftpClient, pathName);
                files = ftpClient.listFiles();
            } catch (IOException e) {
                log.error("Get ftp server file list error.");
            }
            for (FTPFile file : files) {
                if (file.isFile()) {
                    if (file.getName().endsWith(ext)) {
                        fileList.add(pathName + file.getName());
                    }
                } else if (file.isDirectory()) {
                    if (!".".equals(file.getName()) && !"..".equals(file.getName())) {
                        listFileByExt(ftpClient, fileList, pathName + file.getName() + SLASH_STR, ext);
                    }
                }
            }
        }
        return fileList;
    }

    /**
     * upload file
     *
     * @param ftpClient   ftp client
     * @param pathname    ftp server path
     * @param fileName    upload file name
     * @param inputStream inputStream
     * @return operation result
     */
    public static boolean uploadFile(FTPClient ftpClient, String pathname, String fileName, InputStream inputStream) {
        boolean flag = false;
        try {
            log.info("start upload file");
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            ftpClient.enterLocalPassiveMode();
            createDirectory(ftpClient, pathname);
            ftpClient.makeDirectory(pathname);
            ftpClient.setControlEncoding("utf-8");
            ftpClient.storeFile(fileName, inputStream);

            inputStream.close();
            flag = true;
            log.info("upload file successful");

        } catch (Exception e) {
            log.error("failed to upload file");
            e.printStackTrace();
        } finally {
            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return flag;
    }


    /**
     * change working directory
     *
     * @param ftpClient ftp client
     * @param directory ftp directory
     * @return operation result
     */
    public static boolean changeWorkingDirectory(FTPClient ftpClient, String directory) {
        boolean flag;
        try {
            flag = ftpClient.changeWorkingDirectory(directory);
            if (flag) {
                log.info("enter" + directory + " successfully!");
            } else {
                log.info("enter" + directory + " failed!Start to create this directory.");
                throw new CommonException(HttpStatusCode.BAD_REQUEST, NOT_FIND_DIRECTORY);
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
        return true;
    }

    /**
     * Create multilayer directory. If the directory already exists on the FTP server, do not create it. If no, create it.
     *
     * @param ftpClient ftp client
     * @param remote    remote path
     * @throws IOException io exception
     */
    public static void createDirectory(FTPClient ftpClient, String remote) throws IOException {
        String directory = remote + SLASH_STR;
        // If the remote directory does not exist, the remote server directory will be created recursively
        if (!SLASH_STR.equalsIgnoreCase(directory) && !changeWorkingDirectory(ftpClient, directory)) {
            int start = 0;
            int end;
            if (directory.startsWith(SLASH_STR)) {
                start = 1;
            }
            end = directory.indexOf(SLASH_STR, start);
            String path = "";
            do {
                String subDirectory = new String(remote.substring(start, end).getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
                path = path + SLASH_STR + subDirectory;
                if (!existFile(ftpClient, path)) {
                    if (!makeDirectory(ftpClient, subDirectory)) {
                        log.error("create directory[" + subDirectory + "]failed");
                    }
                }
                changeWorkingDirectory(ftpClient, subDirectory);

                start = end + 1;
                end = directory.indexOf(SLASH_STR, start);
                // Check whether all directories are created
            } while (end > start);
        }
    }

    /**
     * Check whether files on the FTP server exist
     *
     * @param ftpClient ftp client
     * @param path      path name
     * @return operation result
     * @throws IOException io exception
     */
    public static boolean existFile(FTPClient ftpClient, String path) throws IOException {
        boolean flag = false;
        FTPFile[] ftpFileArr = ftpClient.listFiles(path);
        if (ftpFileArr.length > 0) {
            flag = true;
        }
        return flag;
    }

    /**
     * Check whether local path exist
     *
     * @param path local path name
     */
    public static void existLocalPath(String path) {
        File directory = new File(path);
        if (!directory.exists()) {
            throw new CommonException(HttpStatusCode.BAD_REQUEST, NOT_FIND_LOCAL_PATH);
        }
        if (!directory.isDirectory()) {
            throw new CommonException(HttpStatusCode.BAD_REQUEST, LOCAL_PATH_NOT_DIRECTORY);
        }
    }

    /**
     * create directory
     *
     * @param ftpClient ftp client
     * @param dir       dir name
     * @return operation result
     */
    public static boolean makeDirectory(FTPClient ftpClient, String dir) {
        boolean flag = true;
        try {
            flag = ftpClient.makeDirectory(dir);
            if (flag) {
                log.info("create directory " + dir + " successful!");
            } else {
                log.error("create directory " + dir + " failed!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return flag;
    }

    /**
     * download all files, not include package by given path
     *
     * @param ftpClient ftp client
     * @param pathName  FTP server path name
     * @param localPath local file path
     * @return operation result
     */
    public static boolean downloadFileByPath(FTPClient ftpClient, String pathName, String localPath) {
        boolean flag = false;
        OutputStream os = null;
        //switch ftp directory
        changeWorkingDirectory(ftpClient, pathName);
        //check local path
        existLocalPath(localPath);
        try {
            log.info("start to download file");
            FTPFile[] ftpFiles = ftpClient.listFiles();
            for (FTPFile file : ftpFiles) {
                if (file.isFile()) {
                    File localFile = new File(localPath + SLASH_STR + file.getName());
                    os = new FileOutputStream(localFile);
                    ftpClient.retrieveFile(file.getName(), os);
                    os.close();
                }
            }
            flag = true;
            log.info("download file successful");
        } catch (Exception e) {
            log.error("download file failed");
            e.printStackTrace();
        } finally {
            if (null != os) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return flag;
    }

    /**
     * download file
     *
     * @param ftpClient ftp client
     * @param pathName  FTP server path name
     * @param fileName  file name
     * @param localPath local file path
     * @return operation result
     */
    public static boolean downloadFile(FTPClient ftpClient, String pathName, String fileName, String localPath) {
        boolean flag = false;
        OutputStream os = null;
        //switch ftp directory
        changeWorkingDirectory(ftpClient, pathName);
        //check local path
        existLocalPath(localPath);
        try {
            log.info("start to download file");
            FTPFile[] ftpFiles = ftpClient.listFiles();
            for (FTPFile file : ftpFiles) {
                if (fileName.equalsIgnoreCase(file.getName())) {
                    File localFile = new File(localPath + SLASH_STR + file.getName());
                    os = new FileOutputStream(localFile);
                    ftpClient.retrieveFile(file.getName(), os);
                    os.close();
                    flag = true;
                }
            }
        } catch (Exception e) {
            log.error("download file failed");
            e.printStackTrace();
        } finally {
            if (null != os) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        if (!flag) {
            throw new CommonException(HttpStatusCode.BAD_REQUEST, NOT_FIND_FILE);
        }

        return true;
    }

    /**
     * delete file
     *
     * @param ftpClient ftp client
     * @param pathname  FTP server path name
     * @param filename  file name
     * @return operation result
     */
    public static boolean deleteFile(FTPClient ftpClient, String pathname, String filename) {
        boolean flag = false;
        try {
            log.info("start to delete file");
            // switch ftp directory
            ftpClient.changeWorkingDirectory(pathname);
            ftpClient.dele(filename);
            flag = true;
            log.info("delete file successful");
        } catch (Exception e) {
            log.info("delete file failed");
            e.printStackTrace();
        }
        return flag;
    }

    /**
     * write inputStream to HtpServletResponse
     *
     * @param input    inputStream
     * @param response HttpServletResponse
     * @return operation result
     */
    public static boolean inputToResponse(InputStream input, HttpServletResponse response) {
        boolean flag = false;
        byte[] buffer = new byte[2048];
        BufferedInputStream bis = null;
        try {
            bis = new BufferedInputStream(input);
            OutputStream os = response.getOutputStream();
            int i = bis.read(buffer);
            while (i != -1) {
                os.write(buffer, 0, i);
                i = bis.read(buffer);
            }
            flag = true;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (bis != null) {
                    bis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return flag;
    }
}
構建一個clientPool,原理是透過ftpClientFactory來建立ftpClient,然後將ftpClient放入一個阻塞佇列中(BlockingQueue<FTPClient> pool)
@Data
public class FtpClientPool {

    private static final int DEFAULT_POOL_SIZE = 16;

    private static final int TIMEOUT = 10;

    private BlockingQueue<FTPClient> pool;

    private FtpClientFactory factory;

    public FtpClientPool(FtpClientFactory factory) {
        this(factory, DEFAULT_POOL_SIZE);
    }

    public FtpClientPool(FtpClientFactory factory, int size) {
        this.factory = factory;
        this.pool = new ArrayBlockingQueue<>(size);
        initPool(size);
    }

    /**
     * Initialize the pool
     *
     * @param maxPoolSize max pool size
     * @throws Exception
     */
    private void initPool(int maxPoolSize) {
        try {
            int count = 0;
            while (count < maxPoolSize) {
                pool.offer(factory.makeClient(), TIMEOUT, TimeUnit.SECONDS);
                count++;
            }
        } catch (Exception e) {
            log.error("init ftp connection pool failed", e);
        }
    }

    public FTPClient borrowClient() {
        FTPClient client = null;
        try {
            client = pool.take();
        } catch (InterruptedException e) {
            log.error("Get connection from ftp connection pool error.");
        }

        if (client == null) {
            client = factory.makeClient();
            returnClient(client);
        } else if (!factory.validateClient(client)) {
            invalidateClient(client);
            client = factory.makeClient();
            returnClient(client);
        }
        return client;
    }

    /**
     * Return the client, or destroy it if it cannot be returned within 10 seconds
     *
     * @param ftpClient ftp client
     */
    public void returnClient(FTPClient ftpClient) {
        try {
            if (ftpClient != null && !pool.offer(ftpClient, TIMEOUT, TimeUnit.SECONDS)) {
                factory.destroyClient(ftpClient);
            }
        } catch (Exception e) {
            log.error("Failed to return client.", e);
        }
    }

    /**
     * Invalidate the client
     *
     * @param ftpClient ftp client
     */
    public void invalidateClient(FTPClient ftpClient) {
        pool.remove(ftpClient);
        factory.destroyClient(ftpClient);
    }

    @PreDestroy
    public void close() throws Exception {
        while (pool.iterator().hasNext()) {
            FTPClient client = pool.take();
            factory.destroyClient(client);
        }
    }

}

定期檢查連線狀態:

@Component
public class FtpClientKeepAlive {

    private static final int CYCLE_TIME = 30 * 1000;

    private KeepAliveThread keepAliveThread;

    private final FtpClientPool ftpClientPool;

    public FtpClientKeepAlive(FtpClientPool ftpClientPool) {
        this.ftpClientPool = ftpClientPool;
    }

    @PostConstruct
    public void init() {
        // Start the heartbeat detection thread
        if (keepAliveThread == null) {
            keepAliveThread = new KeepAliveThread();
            Thread thread = new Thread(keepAliveThread);
            thread.start();
        }
    }

    class KeepAliveThread implements Runnable {
        @Override
        public void run() {
            FTPClient ftpClient = null;
            while (true) {
                try {
                    BlockingQueue<FTPClient> pool = ftpClientPool.getPool();
                    if (pool != null && pool.size() > 0) {
                        Iterator<FTPClient> it = pool.iterator();
                        while (it.hasNext()) {
                            ftpClient = it.next();
                            boolean result = ftpClient.sendNoOp();
                            log.debug("heart beat result: {}", result);
                            if (!result) {
                                ftpClientPool.invalidateClient(ftpClient);
                            }
                        }

                    }
                } catch (Exception e) {
                    log.error("ftp heart beat exception", e);
                    ftpClientPool.invalidateClient(ftpClient);
                }

                try {
                    Thread.sleep(CYCLE_TIME);
                } catch (InterruptedException e) {
                    log.error("ftp sleep exception", e);
                }
            }

        }
    }
}


相關文章