Java從ftp服務器上傳與下載文件的實現(xiàn)
一、背景
業(yè)務需要從ftp服務器上上傳、下載、刪除文件等功能,通過查閱資料及手動敲打代碼,實現(xiàn)了操作ftp的基本功能,有需求的小伙伴可以看看具體的實現(xiàn)過程。
二、ftp介紹
摘自百度百科:文件傳輸協(xié)議(File Transfer Protocol,F(xiàn)TP)是用于在網(wǎng)絡上進行文件傳輸?shù)囊惶讟藴蕝f(xié)議,F(xiàn)TP允許用戶以文件操作的方式(如文件的增、刪、改、查、傳送等)與另一主機相互通信。
三、具體代碼實現(xiàn)
1、引入以下依賴
<!--apache下,包含連接ftp服務器的工具--> <dependency> <groupId>commons-net</groupId> <artifactId>commons-net</artifactId> <version>3.6</version> </dependency>
2、編寫一個FTP工具類
含以下四個方法:
- *獲取一個FtpClinet連接
- *關閉FtpClinet連接
- *下載文件
- *上傳文件
import lombok.extern.slf4j.Slf4j; import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPReply; import java.io.*; import java.nio.charset.StandardCharsets; @Slf4j public class FtpUtil { /** * 獲取一個ftp連接 * @param host ip地址 * @param port 端口 * @param username 用戶名 * @param password 密碼 * @return 返回ftp連接對象 * @throws Exception 連接ftp時發(fā)生的各種異常 */ public static FTPClient getFtpClient(String host, Integer port, String username, String password) throws Exception{ FTPClient ftpClient = new FTPClient(); // 連接服務器 ftpClient.connect(host, port); int reply = ftpClient.getReplyCode(); if(!FTPReply.isPositiveCompletion(reply)){ log.error("無法連接至ftp服務器, host:{}, port:{}", host, port); ftpClient.disconnect(); return null; } // 登入服務器 boolean login = ftpClient.login(username, password); if(!login){ log.error("登錄失敗, 用戶名或密碼錯誤"); ftpClient.logout(); ftpClient.disconnect(); return null; } // 連接并且成功登陸ftp服務器 log.info("login success ftp server, host:{}, port:{}, user:{}", host, port, username); // 設置通道字符集, 要與服務端設置一致 ftpClient.setControlEncoding("UTF-8"); // 設置文件傳輸編碼類型, 字節(jié)傳輸:BINARY_FILE_TYPE, 文本傳輸:ASCII_FILE_TYPE, 建議使用BINARY_FILE_TYPE進行文件傳輸 ftpClient.setFileType(FTP.BINARY_FILE_TYPE); // 動模式: enterLocalActiveMode(),被動模式: enterLocalPassiveMode(),一般選擇被動模式 ftpClient.enterLocalPassiveMode(); // 切換目錄 //ftpClient.changeWorkingDirectory("xxxx"); return ftpClient; } /** * 斷開ftp連接 * @param ftpClient ftp連接客戶端 */ public static void disConnect(FTPClient ftpClient){ if(ftpClient == null){ return; } try { log.info("斷開ftp連接, host:{}, port:{}", ftpClient.getPassiveHost(), ftpClient.getPassivePort()); ftpClient.logout(); ftpClient.disconnect(); } catch (IOException e) { e.printStackTrace(); log.error("ftp連接斷開異常, 請檢查"); } } /** * 文件下載 * @param ftpClient ftp連接客戶端 * @param path 文件路徑 * @param fileName 文件名稱 */ public static void download(FTPClient ftpClient, String path, String fileName) throws Exception { if(ftpClient == null || path == null || fileName == null){ return; } // 中文目錄處理存在問題, 轉化為ftp能夠識別中文的字符集 String remotePath; try { remotePath = new String(path.getBytes(StandardCharsets.UTF_8), FTP.DEFAULT_CONTROL_ENCODING); } catch (UnsupportedEncodingException e) { remotePath = path; } InputStream inputStream = ftpClient.retrieveFileStream(remotePath); if (inputStream == null) { log.error("{}在ftp服務器中不存在,請檢查", path); return; } FileOutputStream outputStream = new FileOutputStream("D:\\test\\" + fileName); BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream); try{ byte[] buffer = new byte[2048]; int i; while ((i = bufferedInputStream.read(buffer)) != -1) { bufferedOutputStream.write(buffer, 0, i); bufferedOutputStream.flush(); } } catch (Exception e) { log.error("文件下載異常", e); log.error("{}下載異常,請檢查", path); } inputStream.close(); outputStream.close(); bufferedInputStream.close(); bufferedOutputStream.close(); // 關閉流之后必須執(zhí)行,否則下一個文件導致流為空 boolean complete = ftpClient.completePendingCommand(); if(complete){ log.info("文件{}下載完成", remotePath); }else{ log.error("文件{}下載失敗", remotePath); } } /** * 上傳文件 * @param ftpClient ftp連接客戶端 * @param sourcePath 源地址 */ public static void upload(FTPClient ftpClient, String sourcePath) throws Exception{ if(ftpClient == null || sourcePath == null){ return; } File file = new File(sourcePath); if(!file.exists() || !file.isFile()){ return; } // 中文目錄處理存在問題, 轉化為ftp能夠識別中文的字符集 String remotePath; try { remotePath = new String(file.getName().getBytes(StandardCharsets.UTF_8), FTP.DEFAULT_CONTROL_ENCODING); } catch (UnsupportedEncodingException e) { remotePath = file.getName(); } try( InputStream inputStream = new FileInputStream(file); OutputStream outputStream = ftpClient.storeFileStream(remotePath); ){ byte[] buffer = new byte[2048]; int length; while((length = inputStream.read(buffer)) != -1){ outputStream.write(buffer, 0, length); outputStream.flush(); } }catch (Exception e){ log.error("文件上傳異常", e); } // 關閉流之后必須執(zhí)行,否則下一個文件導致流為空 boolean complete = ftpClient.completePendingCommand(); if(complete){ log.info("文件{}上傳完成", remotePath); }else{ log.error("文件{}上傳失敗", remotePath); } } }
3、測試連接、上傳文件、下載文件、關閉連接功能
@Slf4j @RestController public class FtpController { private static final String FTP_HOST = "your host"; private static final Integer FTP_PORT = 21; private static final String FTP_USERNAME = "your username"; private static final String FTP_PASSWORD = "your password"; @GetMapping("/test") public void test() throws Exception{ FTPClient ftpClient = FtpUtil.getFtpClient(FTP_HOST, FTP_PORT, FTP_USERNAME, FTP_PASSWORD); // 展示文件夾 FTPFile[] ftpFiles = ftpClient.listDirectories(); for(FTPFile file : ftpFiles){ System.out.println(file.getName()); } // 下載文件 FtpUtil.download(ftpClient, "test.txt", "test.txt"); //FtpUtil.download(ftpClient, "test.py", "test.py"); // 上傳文件 FtpUtil.upload(ftpClient, "D:\\test\\test.py"); FtpUtil.disConnect(ftpClient); } }
經(jīng)過測試,能夠正常連接上ftp服務器,并且上傳下載文件,最后關閉連接。
四、遇到的問題
在使用過程中,遇到以下問題,需要記錄一下,以防下次忘記:
就是在創(chuàng)建完一個FtpClient連接之后,關閉連接之前,第一次進行文件上傳或下載是正常的,當?shù)诙芜M行文件上傳或下載時,從FtpClient獲取的輸入流或輸出流是空的。需要在每次操作完文件上傳或下載時,添加上以下代碼片段:
// 關閉流之后必須執(zhí)行,否則下一個文件導致流為空 boolean complete = ftpClient.completePendingCommand(); if(complete){ log.info("文件{}下載完成", remotePath); }else{ log.error("文件{}下載失敗", remotePath); }
該方法有以下注釋:
There are a few FTPClient methods that do not complete the entire sequence
of FTP commands to complete a transaction. These commands require some action
by the programmer after the reception of a positive intermediate command.
After the programmer's code completes its actions, it must call this method
to receive the completion reply from the server and verify the success of the
entire transaction.
百度翻譯大致意思應該是:有一些FTPClient方法不能完成完成事務的整個FTP命令序列。這些命令需要程序員在接收到肯定的中間命令后采取一些行動。程序員的代碼完成其操作后,必須調(diào)用此方法以從服務器接收完成回復,并驗證整個事務的成功。
這里不知道是不是描述的我這種情況, completePedingCommand的意思是完成掛起命令??赡芪覀儺斍皥鼍熬头?。剛興趣的小伙伴可以深入去探討一下。
五、參考連接
到此這篇關于Java從ftp服務器上傳與下載文件的文章就介紹到這了,更多相關Java ftp服務器上傳下載文件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot去除內(nèi)嵌tomcat的實現(xiàn)
這篇文章主要介紹了SpringBoot去除內(nèi)嵌tomcat的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-09-09Java中的BlockingQueue阻塞隊列原理以及實現(xiàn)詳解
這篇文章主要介紹了Java中的BlockingQueue阻塞隊列原理以及實現(xiàn)詳解,在最常見的使用到這個阻塞隊列的地方,就是我們耳熟能詳?shù)木€程池里面了,作為我們線程池的一大最大參與者,也是AQS的一個具體實現(xiàn),需要的朋友可以參考下2023-12-12JAVA實現(xiàn)遍歷文件夾下的所有文件(遞歸調(diào)用和非遞歸調(diào)用)
本篇文章主要介紹了JAVA 遍歷文件夾下的所有文件(遞歸調(diào)用和非遞歸調(diào)用) ,具有一定的參考價值,有興趣的可以了解一下。2017-01-01Windows同時安裝兩個版本JDK并實現(xiàn)動態(tài)切換JAVA8或JAVA11的方法
這篇文章主要給大家介紹了關于Windows同時安裝兩個版本JDK并實現(xiàn)動態(tài)切換JAVA8或JAVA11的相關資料,文中通過圖文介紹的非常詳細,對大家的學習或工作具有一定的參考學習價值,需要的朋友可以參考下2022-11-11Spring Security自定義登錄原理及實現(xiàn)詳解
這篇文章主要介紹了Spring Security自定義登錄原理及實現(xiàn)詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-09-09