/** * */ package com.common.file; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import org.apache.commons.lang.NullArgumentException; import org.apache.commons.lang.StringUtils; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPReply; import org.apache.log4j.Logger; import com.common.DateUtils; /** * FTP客戶端工具類 * @author luolin * * @version $id:FTPClientUtils.java,v 0.1 2015年11月13日 下午4:18:07 luolin Exp $ */ public class FTPClientUtil { private static final Logger LOGGER = Logger.getLogger(FTPClientUtil.class); /** * 鏈接文件服務器 * @param addr 文件服務器地址 * @param port 端口 * @param username 用戶名 * @param password 密碼 * @throws Exception */ public static FTPClient connect(String addr, int port, String username, String password) { LOGGER.info("【鏈接文件服務器】addr = " + addr + " , port : " + port + " , username = " + username + " , password = " + password); FTPClient ftpClient = new FTPClient(); try { // 鏈接 ftpClient.connect(addr, port); // 登陸 ftpClient.login(username, password); // 被動模式:每次數據鏈接以前,ftp client告訴ftp server開通一個端口來傳輸數據(參考資料:FTP主動/被動模式的解釋) ftpClient.enterLocalPassiveMode(); ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); } catch (Exception e) { LOGGER.error("【鏈接文件服務器失敗】", e); throw new RuntimeException("鏈接文件服務器失敗"); } // 判斷文件服務器是否可用?? if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) { closeConnection(ftpClient); } return ftpClient; } /** * 鏈接文件服務器 * @param addr 文件服務器地址 * @param port 端口 * @param username 用戶名 * @param password 密碼 * @param workingDirectory 目標鏈接工做目錄 * @throws Exception */ public static FTPClient connect(String addr, int port, String username, String password, String workingDirectory) throws Exception { FTPClient ftpClient = connect(addr, port, username, password); changeWorkingDirectory(workingDirectory, ftpClient); return ftpClient; } /** * 關閉鏈接,使用完鏈接以後,必定要關閉鏈接,不然服務器會拋出 Connection reset by peer的錯誤 * @throws IOException */ public static void closeConnection(FTPClient ftpClient) { LOGGER.info("【關閉文件服務器鏈接】"); if (ftpClient == null) { return; } try { ftpClient.disconnect(); } catch (IOException e) { LOGGER.error("【關閉鏈接失敗】", e); throw new RuntimeException("關閉鏈接失敗"); } } /** * 切換工做目錄 * @param directory 目標工做目錄 * @param ftpClient * @throws IOException */ public static void changeWorkingDirectory(String directory, FTPClient ftpClient) { LOGGER.info("【切換工做目錄】directory : " + directory); baseValidate(ftpClient); // 切換到目標工做目錄 try { if (!ftpClient.changeWorkingDirectory(directory)) { ftpClient.makeDirectory(directory); ftpClient.changeWorkingDirectory(directory); } } catch (Throwable e) { LOGGER.error("【切換工做目錄失敗】", e); throw new RuntimeException("切換工做目錄失敗"); } } /** * 上傳文件/文件夾 * @param file 上傳的文件或文件夾 * @return 文件存放的路徑以及文件名 * @throws Exception */ public static void upload(File file, FTPClient ftpClient) throws Exception { if (file == null) { LOGGER.warn("【存儲的文件爲空】"); throw new RuntimeException("上傳文件爲空"); } LOGGER.info("【上傳文件/文件夾】file : " + file.getName()); baseValidate(ftpClient); // 是文件,直接上傳 if (!file.isDirectory()) { storeFile(new File(file.getPath()), ftpClient); return; } changeWorkingDirectory(file.getName(), ftpClient); // 文件夾,遞歸上傳全部文件 for (File item : file.listFiles()) { if (!item.isDirectory()) { storeFile(item, ftpClient); continue; } upload(item, ftpClient); ftpClient.changeToParentDirectory(); } } /** * 刪除文件 * @param fileName 要刪除的文件地址 * @return true/false * @throws IOException */ public static boolean delete(String fileName, FTPClient ftpClient) throws IOException { LOGGER.info("【刪除文件】fileName : " + fileName); baseValidate(ftpClient); if (StringUtils.isBlank(fileName)) { LOGGER.warn("【參數fileName爲空】"); throw new NullArgumentException("fileName"); } return ftpClient.deleteFile(fileName); } /** * 存儲文件 * @param file {@link File} * @throws Exception */ public static void storeFile(File file, FTPClient ftpClient) throws Exception { if (file == null) { LOGGER.warn("【存儲的文件爲空】"); throw new RuntimeException("存儲的文件爲空"); } LOGGER.info("【存儲文件】file : " + file.getName()); baseValidate(ftpClient); FileInputStream input = new FileInputStream(file); ftpClient.storeFile(file.getName(), input); input.close(); } /** * 存儲文件 * @param inputStream {@link InputStream} * @param fileName 文件名 * @throws Exception */ public static void storeFile(InputStream inputStream, String fileName, FTPClient ftpClient) throws Exception { LOGGER.info("【存儲文件】fileName = " + fileName); if (inputStream == null) { LOGGER.warn("【參數inputStream爲空】"); throw new RuntimeException("存儲的文件爲空"); } baseValidate(ftpClient); ftpClient.storeFile(fileName, inputStream); inputStream.close(); } /** * 下載文件到指定目錄 * @param ftpFile 文件服務器上的文件地址 * @param dstFile 輸出文件的路徑和名稱 * @throws Exception */ public static void downLoad(String ftpFile, String dstFile, FTPClient ftpClient) throws Exception { LOGGER.info("【下載文件到指定目錄】ftpFile = " + ftpFile + " , dstFile = " + dstFile); if (StringUtils.isBlank(ftpFile)) { LOGGER.warn("【參數ftpFile爲空】"); throw new RuntimeException("【參數ftpFile爲空】"); } if (StringUtils.isBlank(dstFile)) { LOGGER.warn("【參數dstFile爲空】"); throw new RuntimeException("【參數dstFile爲空】"); } baseValidate(ftpClient); File file = new File(dstFile); FileOutputStream fos = new FileOutputStream(file); ftpClient.retrieveFile(ftpFile, fos); fos.flush(); fos.close(); } /** * 從文件服務器獲取文件流 * @param ftpFile 文件服務器上的文件地址 * @return {@link InputStream} * @throws IOException */ public static InputStream retrieveFileStream(String ftpFile, FTPClient ftpClient) throws IOException { LOGGER.info("【從文件服務器獲取文件流】ftpFile : " + ftpFile); if (StringUtils.isBlank(ftpFile)) { LOGGER.warn("【參數ftpFile爲空】"); throw new RuntimeException("【參數ftpFile爲空】"); } baseValidate(ftpClient); return ftpClient.retrieveFileStream(ftpFile); } /** * 複製文件 * @param ftpClient {@link FTPClient} * @param sourceFile 源文件 * @param targetDir 目標文件夾 * @return 複製後的文件路徑及文件名 * @throws Exception */ public static String copy(FTPClient ftpClient, String sourceFile, String targetDir) throws Exception { return copy(ftpClient, sourceFile, targetDir, FileUtil.getFileNameNoPath(sourceFile)); } /** * 複製文件 * @param ftpClient {@link FTPClient} * @param sourceFile 源文件 * @param targetDir 目標文件夾 * @param newName 新的文件名 * @return 複製後的文件路徑及文件名 * @throws Exception */ public static String copy(FTPClient ftpClient, String sourceFile, String targetDir, String newName) throws Exception { LOGGER.info("【拷貝文件】sourceFile = " + sourceFile + " , targetDir = " + targetDir + " , newName = " + newName); if (StringUtils.isBlank(sourceFile)) { LOGGER.warn("【參數sourceFile爲空】"); throw new NullArgumentException("sourceFile"); } if (StringUtils.isBlank(targetDir)) { LOGGER.warn("【參數targetDir爲空】"); throw new NullArgumentException("targetDir"); } if (StringUtils.isBlank(newName)) { LOGGER.warn("【參數newName爲空】"); throw new NullArgumentException("newName"); } baseValidate(ftpClient); LOGGER.info("【從文件服務器讀取源文件到輸入流中】"); InputStream is = ftpClient.retrieveFileStream(sourceFile); // 主動調用一次getReply()把接下來的226消費掉. 這樣作是能夠解決這個返回null問題 ftpClient.getReply(); if (is == null) { LOGGER.warn("【未找到源文件】"); throw new RuntimeException("未找到源文件"); } LOGGER.info("【將輸入流存儲到指定的目錄】"); changeWorkingDirectory(targetDir, ftpClient); changeWorkingDirectory(DateUtils.getNow(DateUtils.FORMAT_SHORT_FOLDER), ftpClient); storeFile(is, newName, ftpClient); return ftpClient.printWorkingDirectory() + File.separator + newName; } /** * 基本校驗 * @param ftpClient {@link FTPClient} */ private static void baseValidate(FTPClient ftpClient) { if (ftpClient == null) { LOGGER.warn("【參數ftpClient爲空】"); throw new NullArgumentException("ftpClient"); } } }
FTPManager.java代碼:java
/** * */ package com.common.file; import java.io.File; import java.io.IOException; import java.io.InputStream; import javax.annotation.Resource; import org.apache.commons.lang.NullArgumentException; import org.apache.commons.lang.StringUtils; import org.apache.commons.net.ftp.FTPClient; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import com.common.DateUtils; import com.common.property.FileSystemProperties; /** * FTP文件管理器 * @author luolin * * @version $id:FTPManager.java,v 0.1 2015年11月16日 上午11:08:13 luolin Exp $ */ @Component("ftpManager") public class FTPManager { private static final Logger LOGGER = Logger.getLogger(FTPManager.class); /** file.system.properties文件內容映射類 */ @Resource(name = "fileSystemProperties") private FileSystemProperties fileSystemProperties; /** * 上傳文件到FTP服務器 * @param file {@link File} * @return 文件上傳到服務器的地址 */ public String upload(File file) { LOGGER.info("【上傳文件到FTP服務器】"); if (file == null) { LOGGER.warn("【參數file爲空】"); throw new NullArgumentException("file"); } FTPClient ftpClient = connectServer(fileSystemProperties.getProjectWorkingDirectory()); // 切換到子目錄,按日期,天天建立一個目錄 String subFolder = DateUtils.getNow(DateUtils.FORMAT_SHORT_FOLDER); FTPClientUtil.changeWorkingDirectory(subFolder, ftpClient); try { FTPClientUtil.upload(file, ftpClient); } catch (Exception e) { LOGGER.error("【文件上傳失敗】", e); throw new RuntimeException("文件上傳失敗"); } finally { FTPClientUtil.closeConnection(ftpClient); } String uploadedFilePath = fileSystemProperties.getProjectWorkingDirectory() + File.separator + subFolder + File.separator + file.getName(); LOGGER.info("【文件上傳完成】uploadedFilePath = " + uploadedFilePath); return uploadedFilePath; } /** * 上傳文件到FTP服務器 * @param file {@link File} * @param targetFolder 目標子目錄 * @return 文件上傳到服務器的地址 */ public String upload(File file, String targetFolder) { LOGGER.info("【上傳文件到FTP服務器】targetFolder : " + targetFolder); if (file == null) { LOGGER.warn("【參數file爲空】"); throw new NullArgumentException("file"); } FTPClient ftpClient = connectServer(fileSystemProperties.getProjectWorkingDirectory()); // 切換到子目錄,按日期,天天建立一個目錄 String subFolder = DateUtils.getNow(DateUtils.FORMAT_SHORT_FOLDER); FTPClientUtil.changeWorkingDirectory(targetFolder, ftpClient); FTPClientUtil.changeWorkingDirectory(subFolder, ftpClient); try { FTPClientUtil.upload(file, ftpClient); } catch (Exception e) { LOGGER.error("【文件上傳失敗】", e); throw new RuntimeException("文件上傳失敗"); } finally { FTPClientUtil.closeConnection(ftpClient); } String uploadedFilePath = fileSystemProperties.getProjectWorkingDirectory() + File.separator + targetFolder + File.separator + subFolder + File.separator + file.getName(); LOGGER.info("【文件上傳完成】uploadedFilePath = " + uploadedFilePath); return uploadedFilePath; } /** * 鏈接服務器 * @return {@link FTPClient} */ private FTPClient connectServer(String workingDirectory) { FTPClient ftpClient = null; try { // 鏈接服務器,並切換到對應的項目文件存儲目錄 ftpClient = FTPClientUtil.connect(fileSystemProperties.getHost(), fileSystemProperties.getPort(), fileSystemProperties.getUsername(), fileSystemProperties.getPwd(), workingDirectory); } catch (Exception e) { LOGGER.error("【鏈接服務器失敗】", e); throw new RuntimeException("鏈接服務器失敗"); } return ftpClient; } /** * 鏈接服務器 * @return {@link FTPClient} */ private FTPClient connectServer() { FTPClient ftpClient = null; try { // 鏈接服務器,並切換到對應的項目文件存儲目錄 ftpClient = FTPClientUtil.connect(fileSystemProperties.getHost(), fileSystemProperties.getPort(), fileSystemProperties.getUsername(), fileSystemProperties.getPwd()); } catch (Exception e) { LOGGER.error("【鏈接服務器失敗】", e); throw new RuntimeException("鏈接服務器失敗"); } return ftpClient; } /** * 從服務器獲取文件輸入流 * @param fileName 文件路徑和名稱 * @return {@link InputStream} */ public InputStream getFileInputStream(String fileName) { LOGGER.info("【從服務器獲取文件輸入流】fileName = " + fileName); if (StringUtils.isBlank(fileName)) { LOGGER.warn("【參數fileName爲空】"); throw new NullArgumentException("fileName"); } FTPClient ftpClient = connectServer(); try { return FTPClientUtil.retrieveFileStream(fileName, ftpClient); } catch (IOException e) { LOGGER.error("【獲取文件流失敗】", e); throw new RuntimeException("獲取文件流失敗"); } finally { FTPClientUtil.closeConnection(ftpClient); } } /** * 從文件服務器文件下載到應用服務器本地 * @param fileName 文件路徑及名稱 * @param tmpPath 臨時文件存放目錄 * @return 存儲到臨時文件目錄的完整路徑 */ public String downloadFileToLocal(String fileName, String tmpPath) { LOGGER.info("【從文件服務器文件下載到應用服務器本地】fileName = " + fileName); if (StringUtils.isBlank(fileName)) { LOGGER.warn("【參數fileName爲空】"); throw new NullArgumentException("fileName"); } FTPClient ftpClient = connectServer(); String name = FileUtil.getFileNameNoPath(fileName); try { FTPClientUtil.downLoad(fileName, tmpPath + File.separator + name, ftpClient); } catch (Exception e) { LOGGER.error("【下載文件失敗】", e); throw new RuntimeException("下載文件失敗"); } finally { FTPClientUtil.closeConnection(ftpClient); } return tmpPath + File.separator + name; } /** * 刪除文件服務器的文件 * @param fileName 文件在文件服務器的地址 * @throws IOException */ public void delete(String fileName) throws IOException { LOGGER.info("【刪除文件服務器的文件】fileName = " + fileName); FTPClientUtil.delete(fileName, connectServer()); } /** * 文件拷貝 * @param sourceFile 源文件地址 * @param targetDir 目標文件夾 * @return 文件拷貝後的完整路徑 */ public String copy(String sourceFile, String targetDir) { LOGGER.info("【文件拷貝】sourceFile = " + sourceFile + " , targetDir = " + targetDir); if (StringUtils.isBlank(sourceFile)) { LOGGER.warn("【參數sourceFile爲空】"); throw new NullArgumentException("sourceFile"); } if (StringUtils.isBlank(targetDir)) { LOGGER.warn("【參數targetDir爲空】"); throw new NullArgumentException("targetDir"); } try { return FTPClientUtil.copy(connectServer(), sourceFile, fileSystemProperties.getProjectWorkingDirectory() + File.separator + targetDir); } catch (Exception e) { LOGGER.error("【文件拷貝失敗】", e); throw new RuntimeException("文件拷貝失敗"); } } }
下面也給出我測試的時候,寫的demo的代碼spring
/** * */ package com.eay.ftp; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPReply; /** * FTP客戶端工具類示例 * @author ll * * @version $id:FTPClientExample.java,v 0.1 2015年11月13日 上午11:32:24 ll Exp $ */ public class FTPClientExample { /** FTP客戶端實例 */ private FTPClient ftpClient; /** * 私有構造器 */ private FTPClientExample() { } /** * 內部類維護單例,防止併發問題 * @author luolin * * @version $id:FTPClientExample.java,v 0.1 2015年11月13日 下午2:34:08 luolin Exp $ */ private static class SingletonFactory { private static FTPClientExample instance = new FTPClientExample(); } /** * 獲取實例 * @return {@link FTPClientExample} */ public static FTPClientExample getInstance() { return SingletonFactory.instance; } /** * 鏈接文件服務器 * @param addr 文件服務器地址 * @param port 端口 * @param username 用戶名 * @param password 密碼 * @throws Exception */ public void connect(String addr, int port, String username, String password) throws Exception { ftpClient = new FTPClient(); // 鏈接 ftpClient.connect(addr, port); // 登陸 ftpClient.login(username, password); ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); // 判斷文件服務器是否可用?? if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) { ftpClient.disconnect(); } } /** * 鏈接文件服務器 * @param addr 文件服務器地址 * @param port 端口 * @param username 用戶名 * @param password 密碼 * @param workingDirectory 目標鏈接工做目錄 * @throws Exception */ public void connect(String addr, int port, String username, String password, String workingDirectory) throws Exception { ftpClient = new FTPClient(); // 鏈接 ftpClient.connect(addr, port); // 登陸 ftpClient.login(username, password); ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); // 判斷文件服務器是否可用?? if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) { ftpClient.disconnect(); } changeWorkingDirectory(workingDirectory); } /** * 關閉鏈接,使用完鏈接以後,必定要關閉鏈接,不然服務器會拋出 Connection reset by peer的錯誤 * @throws IOException */ public void closeConnection() throws IOException { ftpClient.disconnect(); } /** * 切換工做目錄 * @param directory 目標工做目錄 * @throws IOException */ public void changeWorkingDirectory(String directory) throws IOException { // 切換到目標工做目錄 if (!ftpClient.changeWorkingDirectory(directory)) { ftpClient.makeDirectory(directory); ftpClient.changeWorkingDirectory(directory); } } /** * @param file 上傳的文件或文件夾 * @throws Exception */ public void upload(File file) throws Exception { if (file == null) { throw new RuntimeException("上傳文件爲空"); } // 是文件,直接上傳 if (!file.isDirectory()) { storeFile(new File(file.getPath())); return; } ftpClient.makeDirectory(file.getName()); ftpClient.changeWorkingDirectory(file.getName()); // 文件夾,遞歸上傳全部文件 for (File item : file.listFiles()) { if (!item.isDirectory()) { storeFile(item); continue; } upload(item); ftpClient.changeToParentDirectory(); } } /** * 刪除文件 * @param fileName 要刪除的文件地址 * @return true/false * @throws Exception */ public boolean delete(String fileName) throws Exception { return ftpClient.deleteFile(fileName); } /** * 存儲文件 * @param file {@link File} * @throws Exception */ private void storeFile(File file) throws Exception { FileInputStream input = new FileInputStream(file); ftpClient.storeFile(file.getName(), input); input.close(); } /** * 下載文件 * @param ftpFile 文件服務器上的文件地址 * @param dstFile 輸出文件的路徑和名稱 * @throws Exception */ public void downLoad(String ftpFile, String dstFile) throws Exception { File file = new File(dstFile); FileOutputStream fos = new FileOutputStream(file); ftpClient.retrieveFile(ftpFile, fos); fos.flush(); fos.close(); } /** * 從文件服務器獲取文件流 * @param ftpFile 文件服務器上的文件地址 * @return {@link InputStream} * @throws IOException */ public InputStream retrieveFileStream(String ftpFile) throws IOException { return ftpClient.retrieveFileStream(ftpFile); } public static void main(String[] args) throws Exception { FTPClientExample emp = FTPClientExample.getInstance(); String addr = "192.168.111.60"; int port = 21; String username = "admin"; String password = "admin"; emp.connect(addr, port, username, password); // 上傳文件 emp.changeWorkingDirectory("bss"); emp.upload(new File("E:\\example.txt")); // 下載文件到指定目錄 // emp.downLoad("bss\\example.txt", "E:\\example2.txt"); // 刪除文件 // emp.delete("bss\\example.txt"); // 關閉鏈接,防止文件服務器拋出 Connection reset by peer的錯誤 emp.closeConnection(); } }
<!--ftp -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.3</version>
</dependency>apache