java操作FTP伺服器通用工具類
阿新 • • 發佈:2018-11-06
package cn.com.test.util; import java.io.ByteArrayOutputStream; 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.HashMap; import java.util.Map; import org.apache.commons.io.IOUtils; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPReply; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * FTP服務工具類 * * @author: Rodge * @time: 2017年12月24日 下午23:02:37 * @version: V1.0.0 */ @Component public class FTPUtil { /** 日誌物件 **/ private static final Logger LOGGER = LoggerFactory.getLogger(FTPUtil.class); /** FTP地址 **/ private static final String FTP_ADDRESS = "127.0.0.1"; /** FTP埠 **/ private static final int FTP_PORT = 21; /** FTP使用者名稱 **/ private static final String FTP_USERNAME = "root"; /** FTP密碼 **/ private static final String FTP_PASSWORD = "root"; /** FTP基礎目錄 **/ private static final String BASE_PATH = "ftp/"; /** 本地字元編碼 **/ private static String localCharset = "GBK"; /** FTP協議裡面,規定檔名編碼為iso-8859-1 **/ private static String serverCharset = "ISO-8859-1"; /** UTF-8字元編碼 **/ private static final String CHARSET_UTF8 = "UTF-8"; /** OPTS UTF8字串常量 **/ private static final String OPTS_UTF8 = "OPTS UTF8"; /** 設定緩衝區大小4M **/ private static final int BUFFER_SIZE = 1024 * 1024 * 4; /** FTPClient物件 **/ private static FTPClient ftpClient = null; /** * 本地檔案上傳到FTP伺服器 * * @param ftpPath FTP伺服器檔案相對路徑,例如:test/123 * @param savePath 本地檔案路徑,例如:D:/test/123/test.txt * @param fileName 上傳到FTP服務的檔名,例如:666.txt * @return boolean 成功返回true,否則返回false */ public boolean uploadLocalFile(String ftpPath, String savePath, String fileName) { // 登入 login(FTP_ADDRESS, FTP_PORT, FTP_USERNAME, FTP_PASSWORD); boolean flag = false; if (ftpClient != null) { File file = new File(savePath); FileInputStream fis = null; try { fis = new FileInputStream(file); ftpClient.setBufferSize(BUFFER_SIZE); // 設定編碼:開啟伺服器對UTF-8的支援,如果伺服器支援就用UTF-8編碼,否則就使用本地編碼(GBK) if (FTPReply.isPositiveCompletion(ftpClient.sendCommand(OPTS_UTF8, "ON"))) { localCharset = CHARSET_UTF8; } ftpClient.setControlEncoding(localCharset); ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); String path = changeEncoding(BASE_PATH + ftpPath); // 目錄不存在,則遞迴建立 if (!ftpClient.changeWorkingDirectory(path)) { this.createDirectorys(path); } // 設定被動模式,開通一個埠來傳輸資料 ftpClient.enterLocalPassiveMode(); // 上傳檔案 flag = ftpClient.storeFile(new String(fileName.getBytes(localCharset), serverCharset), fis); } catch (Exception e) { LOGGER.error("本地檔案上傳FTP失敗", e); } finally { IOUtils.closeQuietly(fis); closeConnect(); } } return flag; } /** * 遠端檔案上傳到FTP伺服器 * * @param ftpPath FTP伺服器檔案相對路徑,例如:test/123 * @param remotePath 遠端檔案路徑,例如:http://www.baidu.com/xxx/xxx.jpg * @param fileName 上傳到FTP服務的檔名,例如:test.jpg * @return boolean 成功返回true,否則返回false */ public boolean uploadRemoteFile(String ftpPath, String remotePath, String fileName) { // 登入 login(FTP_ADDRESS, FTP_PORT, FTP_USERNAME, FTP_PASSWORD); boolean flag = false; if (ftpClient != null) { CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; try { // 遠端獲取檔案輸入流 HttpGet httpget = new HttpGet(remotePath); response = httpClient.execute(httpget); HttpEntity entity = response.getEntity(); InputStream input = entity.getContent(); ftpClient.setBufferSize(BUFFER_SIZE); // 設定編碼:開啟伺服器對UTF-8的支援,如果伺服器支援就用UTF-8編碼,否則就使用本地編碼(GBK) if (FTPReply.isPositiveCompletion(ftpClient.sendCommand(OPTS_UTF8, "ON"))) { localCharset = CHARSET_UTF8; } ftpClient.setControlEncoding(localCharset); ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); String path = changeEncoding(BASE_PATH + ftpPath); // 目錄不存在,則遞迴建立 if (!ftpClient.changeWorkingDirectory(path)) { this.createDirectorys(path); } // 設定被動模式,開通一個埠來傳輸資料 ftpClient.enterLocalPassiveMode(); // 上傳檔案 flag = ftpClient.storeFile(new String(fileName.getBytes(localCharset), serverCharset), input); } catch (Exception e) { LOGGER.error("遠端檔案上傳FTP失敗", e); } finally { closeConnect(); try { httpClient.close(); } catch (IOException e) { LOGGER.error("關閉流失敗", e); } if (response != null) { try { response.close(); } catch (IOException e) { LOGGER.error("關閉流失敗", e); } } } } return flag; } /** * 下載指定檔案到本地 * * @param ftpPath FTP伺服器檔案相對路徑,例如:test/123 * @param fileName 要下載的檔名,例如:test.txt * @param savePath 儲存檔案到本地的路徑,例如:D:/test * @return 成功返回true,否則返回false */ public boolean downloadFile(String ftpPath, String fileName, String savePath) { // 登入 login(FTP_ADDRESS, FTP_PORT, FTP_USERNAME, FTP_PASSWORD); boolean flag = false; if (ftpClient != null) { try { String path = changeEncoding(BASE_PATH + ftpPath); // 判斷是否存在該目錄 if (!ftpClient.changeWorkingDirectory(path)) { LOGGER.error(BASE_PATH + ftpPath + "該目錄不存在"); return flag; } ftpClient.enterLocalPassiveMode(); // 設定被動模式,開通一個埠來傳輸資料 String[] fs = ftpClient.listNames(); // 判斷該目錄下是否有檔案 if (fs == null || fs.length == 0) { LOGGER.error(BASE_PATH + ftpPath + "該目錄下沒有檔案"); return flag; } for (String ff : fs) { String ftpName = new String(ff.getBytes(serverCharset), localCharset); if (ftpName.equals(fileName)) { File file = new File(savePath + '/' + ftpName); try (OutputStream os = new FileOutputStream(file)) { flag = ftpClient.retrieveFile(ff, os); } catch (Exception e) { LOGGER.error(e.getMessage(), e); } break; } } } catch (IOException e) { LOGGER.error("下載檔案失敗", e); } finally { closeConnect(); } } return flag; } /** * 下載該目錄下所有檔案到本地 * * @param ftpPath FTP伺服器上的相對路徑,例如:test/123 * @param savePath 儲存檔案到本地的路徑,例如:D:/test * @return 成功返回true,否則返回false */ public boolean downloadFiles(String ftpPath, String savePath) { // 登入 login(FTP_ADDRESS, FTP_PORT, FTP_USERNAME, FTP_PASSWORD); boolean flag = false; if (ftpClient != null) { try { String path = changeEncoding(BASE_PATH + ftpPath); // 判斷是否存在該目錄 if (!ftpClient.changeWorkingDirectory(path)) { LOGGER.error(BASE_PATH + ftpPath + "該目錄不存在"); return flag; } ftpClient.enterLocalPassiveMode(); // 設定被動模式,開通一個埠來傳輸資料 String[] fs = ftpClient.listNames(); // 判斷該目錄下是否有檔案 if (fs == null || fs.length == 0) { LOGGER.error(BASE_PATH + ftpPath + "該目錄下沒有檔案"); return flag; } for (String ff : fs) { String ftpName = new String(ff.getBytes(serverCharset), localCharset); File file = new File(savePath + '/' + ftpName); try (OutputStream os = new FileOutputStream(file)) { ftpClient.retrieveFile(ff, os); } catch (Exception e) { LOGGER.error(e.getMessage(), e); } } flag = true; } catch (IOException e) { LOGGER.error("下載檔案失敗", e); } finally { closeConnect(); } } return flag; } /** * 獲取該目錄下所有檔案,以位元組陣列返回 * * @param ftpPath FTP伺服器上檔案所在相對路徑,例如:test/123 * @return Map<String, Object> 其中key為檔名,value為位元組陣列物件 */ public Map<String, byte[]> getFileBytes(String ftpPath) { // 登入 login(FTP_ADDRESS, FTP_PORT, FTP_USERNAME, FTP_PASSWORD); Map<String, byte[]> map = new HashMap<>(); if (ftpClient != null) { try { String path = changeEncoding(BASE_PATH + ftpPath); // 判斷是否存在該目錄 if (!ftpClient.changeWorkingDirectory(path)) { LOGGER.error(BASE_PATH + ftpPath + "該目錄不存在"); return map; } ftpClient.enterLocalPassiveMode(); // 設定被動模式,開通一個埠來傳輸資料 String[] fs = ftpClient.listNames(); // 判斷該目錄下是否有檔案 if (fs == null || fs.length == 0) { LOGGER.error(BASE_PATH + ftpPath + "該目錄下沒有檔案"); return map; } for (String ff : fs) { try (InputStream is = ftpClient.retrieveFileStream(ff)) { String ftpName = new String(ff.getBytes(serverCharset), localCharset); ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); byte[] buffer = new byte[BUFFER_SIZE]; int readLength = 0; while ((readLength = is.read(buffer, 0, BUFFER_SIZE)) > 0) { byteStream.write(buffer, 0, readLength); } map.put(ftpName, byteStream.toByteArray()); ftpClient.completePendingCommand(); // 處理多個檔案 } catch (Exception e) { LOGGER.error(e.getMessage(), e); } } } catch (IOException e) { LOGGER.error("獲取檔案失敗", e); } finally { closeConnect(); } } return map; } /** * 根據名稱獲取檔案,以位元組陣列返回 * * @param ftpPath FTP伺服器檔案相對路徑,例如:test/123 * @param fileName 檔名,例如:test.xls * @return byte[] 位元組陣列物件 */ public byte[] getFileBytesByName(String ftpPath, String fileName) { // 登入 login(FTP_ADDRESS, FTP_PORT, FTP_USERNAME, FTP_PASSWORD); ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); if (ftpClient != null) { try { String path = changeEncoding(BASE_PATH + ftpPath); // 判斷是否存在該目錄 if (!ftpClient.changeWorkingDirectory(path)) { LOGGER.error(BASE_PATH + ftpPath + "該目錄不存在"); return byteStream.toByteArray(); } ftpClient.enterLocalPassiveMode(); // 設定被動模式,開通一個埠來傳輸資料 String[] fs = ftpClient.listNames(); // 判斷該目錄下是否有檔案 if (fs == null || fs.length == 0) { LOGGER.error(BASE_PATH + ftpPath + "該目錄下沒有檔案"); return byteStream.toByteArray(); } for (String ff : fs) { String ftpName = new String(ff.getBytes(serverCharset), localCharset); if (ftpName.equals(fileName)) { try (InputStream is = ftpClient.retrieveFileStream(ff);) { byte[] buffer = new byte[BUFFER_SIZE]; int len = -1; while ((len = is.read(buffer, 0, BUFFER_SIZE)) != -1) { byteStream.write(buffer, 0, len); } } catch (Exception e) { LOGGER.error(e.getMessage(), e); } break; } } } catch (IOException e) { LOGGER.error("獲取檔案失敗", e); } finally { closeConnect(); } } return byteStream.toByteArray(); } /** * 獲取該目錄下所有檔案,以輸入流返回 * * @param ftpPath FTP伺服器上檔案相對路徑,例如:test/123 * @return Map<String, InputStream> 其中key為檔名,value為輸入流物件 */ public Map<String, InputStream> getFileInputStream(String ftpPath) { // 登入 login(FTP_ADDRESS, FTP_PORT, FTP_USERNAME, FTP_PASSWORD); Map<String, InputStream> map = new HashMap<>(); if (ftpClient != null) { try { String path = changeEncoding(BASE_PATH + ftpPath); // 判斷是否存在該目錄 if (!ftpClient.changeWorkingDirectory(path)) { LOGGER.error(BASE_PATH + ftpPath + "該目錄不存在"); return map; } ftpClient.enterLocalPassiveMode(); // 設定被動模式,開通一個埠來傳輸資料 String[] fs = ftpClient.listNames(); // 判斷該目錄下是否有檔案 if (fs == null || fs.length == 0) { LOGGER.error(BASE_PATH + ftpPath + "該目錄下沒有檔案"); return map; } for (String ff : fs) { String ftpName = new String(ff.getBytes(serverCharset), localCharset); InputStream is = ftpClient.retrieveFileStream(ff); map.put(ftpName, is); ftpClient.completePendingCommand(); // 處理多個檔案 } } catch (IOException e) { LOGGER.error("獲取檔案失敗", e); } finally { closeConnect(); } } return map; } /** * 根據名稱獲取檔案,以輸入流返回 * * @param ftpPath FTP伺服器上檔案相對路徑,例如:test/123 * @param fileName 檔名,例如:test.txt * @return InputStream 輸入流物件 */ public InputStream getInputStreamByName(String ftpPath, String fileName) { // 登入 login(FTP_ADDRESS, FTP_PORT, FTP_USERNAME, FTP_PASSWORD); InputStream input = null; if (ftpClient != null) { try { String path = changeEncoding(BASE_PATH + ftpPath); // 判斷是否存在該目錄 if (!ftpClient.changeWorkingDirectory(path)) { LOGGER.error(BASE_PATH + ftpPath + "該目錄不存在"); return input; } ftpClient.enterLocalPassiveMode(); // 設定被動模式,開通一個埠來傳輸資料 String[] fs = ftpClient.listNames(); // 判斷該目錄下是否有檔案 if (fs == null || fs.length == 0) { LOGGER.error(BASE_PATH + ftpPath + "該目錄下沒有檔案"); return input; } for (String ff : fs) { String ftpName = new String(ff.getBytes(serverCharset), localCharset); if (ftpName.equals(fileName)) { input = ftpClient.retrieveFileStream(ff); break; } } } catch (IOException e) { LOGGER.error("獲取檔案失敗", e); } finally { closeConnect(); } } return input; } /** * 刪除指定檔案 * * @param filePath 檔案相對路徑,例如:test/123/test.txt * @return 成功返回true,否則返回false */ public boolean deleteFile(String filePath) { // 登入 login(FTP_ADDRESS, FTP_PORT, FTP_USERNAME, FTP_PASSWORD); boolean flag = false; if (ftpClient != null) { try { String path = changeEncoding(BASE_PATH + filePath); flag = ftpClient.deleteFile(path); } catch (IOException e) { LOGGER.error("刪除檔案失敗", e); } finally { closeConnect(); } } return flag; } /** * 刪除目錄下所有檔案 * * @param dirPath 檔案相對路徑,例如:test/123 * @return 成功返回true,否則返回false */ public boolean deleteFiles(String dirPath) { // 登入 login(FTP_ADDRESS, FTP_PORT, FTP_USERNAME, FTP_PASSWORD); boolean flag = false; if (ftpClient != null) { try { ftpClient.enterLocalPassiveMode(); // 設定被動模式,開通一個埠來傳輸資料 String path = changeEncoding(BASE_PATH + dirPath); String[] fs = ftpClient.listNames(path); // 判斷該目錄下是否有檔案 if (fs == null || fs.length == 0) { LOGGER.error(BASE_PATH + dirPath + "該目錄下沒有檔案"); return flag; } for (String ftpFile : fs) { ftpClient.deleteFile(ftpFile); } flag = true; } catch (IOException e) { LOGGER.error("刪除檔案失敗", e); } finally { closeConnect(); } } return flag; } /** * 連線FTP伺服器 * * @param address 地址,如:127.0.0.1 * @param port 埠,如:21 * @param username 使用者名稱,如:root * @param password 密碼,如:root */ private void login(String address, int port, String username, String password) { ftpClient = new FTPClient(); try { ftpClient.connect(address, port); ftpClient.login(username, password); ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); int reply = ftpClient.getReplyCode(); if (!FTPReply.isPositiveCompletion(reply)) { closeConnect(); LOGGER.error("FTP伺服器連線失敗"); } } catch (Exception e) { LOGGER.error("FTP登入失敗", e); } } /** * 關閉FTP連線 * */ private void closeConnect() { if (ftpClient != null && ftpClient.isConnected()) { try { ftpClient.logout(); ftpClient.disconnect(); } catch (IOException e) { LOGGER.error("關閉FTP連線失敗", e); } } } /** * FTP伺服器路徑編碼轉換 * * @param ftpPath FTP伺服器路徑 * @return String */ private static String changeEncoding(String ftpPath) { String directory = null; try { if (FTPReply.isPositiveCompletion(ftpClient.sendCommand(OPTS_UTF8, "ON"))) { localCharset = CHARSET_UTF8; } directory = new String(ftpPath.getBytes(localCharset), serverCharset); } catch (Exception e) { LOGGER.error("路徑編碼轉換失敗", e); } return directory; } /** * 在伺服器上遞迴建立目錄 * * @param dirPath 上傳目錄路徑 * @return */ private void createDirectorys(String dirPath) { try { if (!dirPath.endsWith("/")) { dirPath += "/"; } String directory = dirPath.substring(0, dirPath.lastIndexOf("/") + 1); ftpClient.makeDirectory("/"); int start = 0; int end = 0; if (directory.startsWith("/")) { start = 1; }else{ start = 0; } end = directory.indexOf("/", start); while(true) { String subDirectory = new String(dirPath.substring(start, end)); if (!ftpClient.changeWorkingDirectory(subDirectory)) { if (ftpClient.makeDirectory(subDirectory)) { ftpClient.changeWorkingDirectory(subDirectory); } else { LOGGER.info("建立目錄失敗"); return; } } start = end + 1; end = directory.indexOf("/", start); //檢查所有目錄是否建立完畢 if (end <= start) { break; } } } catch (Exception e) { LOGGER.error("上傳目錄建立失敗", e); } } }