Java從ftp服務(wù)器上傳與下載文件的實(shí)現(xiàn)
一、背景
業(yè)務(wù)需要從ftp服務(wù)器上上傳、下載、刪除文件等功能,通過查閱資料及手動(dòng)敲打代碼,實(shí)現(xiàn)了操作ftp的基本功能,有需求的小伙伴可以看看具體的實(shí)現(xiàn)過程。
二、ftp介紹
摘自百度百科:文件傳輸協(xié)議(File Transfer Protocol,F(xiàn)TP)是用于在網(wǎng)絡(luò)上進(jìn)行文件傳輸?shù)囊惶讟?biāo)準(zhǔn)協(xié)議,F(xiàn)TP允許用戶以文件操作的方式(如文件的增、刪、改、查、傳送等)與另一主機(jī)相互通信。
三、具體代碼實(shí)現(xiàn)
1、引入以下依賴
<!--apache下,包含連接ftp服務(wù)器的工具--> <dependency> <groupId>commons-net</groupId> <artifactId>commons-net</artifactId> <version>3.6</version> </dependency>
2、編寫一個(gè)FTP工具類
含以下四個(gè)方法:
- *獲取一個(gè)FtpClinet連接
- *關(guān)閉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 { /** * 獲取一個(gè)ftp連接 * @param host ip地址 * @param port 端口 * @param username 用戶名 * @param password 密碼 * @return 返回ftp連接對象 * @throws Exception 連接ftp時(shí)發(fā)生的各種異常 */ public static FTPClient getFtpClient(String host, Integer port, String username, String password) throws Exception{ FTPClient ftpClient = new FTPClient(); // 連接服務(wù)器 ftpClient.connect(host, port); int reply = ftpClient.getReplyCode(); if(!FTPReply.isPositiveCompletion(reply)){ log.error("無法連接至ftp服務(wù)器, host:{}, port:{}", host, port); ftpClient.disconnect(); return null; } // 登入服務(wù)器 boolean login = ftpClient.login(username, password); if(!login){ log.error("登錄失敗, 用戶名或密碼錯(cuò)誤"); ftpClient.logout(); ftpClient.disconnect(); return null; } // 連接并且成功登陸ftp服務(wù)器 log.info("login success ftp server, host:{}, port:{}, user:{}", host, port, username); // 設(shè)置通道字符集, 要與服務(wù)端設(shè)置一致 ftpClient.setControlEncoding("UTF-8"); // 設(shè)置文件傳輸編碼類型, 字節(jié)傳輸:BINARY_FILE_TYPE, 文本傳輸:ASCII_FILE_TYPE, 建議使用BINARY_FILE_TYPE進(jìn)行文件傳輸 ftpClient.setFileType(FTP.BINARY_FILE_TYPE); // 動(dòng)模式: enterLocalActiveMode(),被動(dòng)模式: enterLocalPassiveMode(),一般選擇被動(dòng)模式 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; } // 中文目錄處理存在問題, 轉(zhuǎn)化為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服務(wù)器中不存在,請檢查", 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(); // 關(guān)閉流之后必須執(zhí)行,否則下一個(gè)文件導(dǎo)致流為空 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; } // 中文目錄處理存在問題, 轉(zhuǎn)化為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); } // 關(guān)閉流之后必須執(zhí)行,否則下一個(gè)文件導(dǎo)致流為空 boolean complete = ftpClient.completePendingCommand(); if(complete){ log.info("文件{}上傳完成", remotePath); }else{ log.error("文件{}上傳失敗", remotePath); } } }
3、測試連接、上傳文件、下載文件、關(guān)閉連接功能
@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服務(wù)器,并且上傳下載文件,最后關(guān)閉連接。
四、遇到的問題
在使用過程中,遇到以下問題,需要記錄一下,以防下次忘記:
就是在創(chuàng)建完一個(gè)FtpClient連接之后,關(guān)閉連接之前,第一次進(jìn)行文件上傳或下載是正常的,當(dāng)?shù)诙芜M(jìn)行文件上傳或下載時(shí),從FtpClient獲取的輸入流或輸出流是空的。需要在每次操作完文件上傳或下載時(shí),添加上以下代碼片段:
// 關(guān)閉流之后必須執(zhí)行,否則下一個(gè)文件導(dǎo)致流為空 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.
百度翻譯大致意思應(yīng)該是:有一些FTPClient方法不能完成完成事務(wù)的整個(gè)FTP命令序列。這些命令需要程序員在接收到肯定的中間命令后采取一些行動(dòng)。程序員的代碼完成其操作后,必須調(diào)用此方法以從服務(wù)器接收完成回復(fù),并驗(yàn)證整個(gè)事務(wù)的成功。
這里不知道是不是描述的我這種情況, completePedingCommand的意思是完成掛起命令??赡芪覀儺?dāng)前場景就符合。剛興趣的小伙伴可以深入去探討一下。
五、參考連接
到此這篇關(guān)于Java從ftp服務(wù)器上傳與下載文件的文章就介紹到這了,更多相關(guān)Java ftp服務(wù)器上傳下載文件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot去除內(nèi)嵌tomcat的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot去除內(nèi)嵌tomcat的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09Java中的BlockingQueue阻塞隊(duì)列原理以及實(shí)現(xiàn)詳解
這篇文章主要介紹了Java中的BlockingQueue阻塞隊(duì)列原理以及實(shí)現(xiàn)詳解,在最常見的使用到這個(gè)阻塞隊(duì)列的地方,就是我們耳熟能詳?shù)木€程池里面了,作為我們線程池的一大最大參與者,也是AQS的一個(gè)具體實(shí)現(xiàn),需要的朋友可以參考下2023-12-12Java方法的參數(shù)傳遞機(jī)制實(shí)例詳解
這篇文章主要介紹了Java方法的參數(shù)傳遞機(jī)制,結(jié)合實(shí)例形式詳細(xì)分析了java方法參數(shù)傳遞機(jī)制原理、實(shí)現(xiàn)方法及操作注意事項(xiàng),需要的朋友可以參考下2019-09-09JAVA實(shí)現(xiàn)遍歷文件夾下的所有文件(遞歸調(diào)用和非遞歸調(diào)用)
本篇文章主要介紹了JAVA 遍歷文件夾下的所有文件(遞歸調(diào)用和非遞歸調(diào)用) ,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-01-01Windows同時(shí)安裝兩個(gè)版本JDK并實(shí)現(xiàn)動(dòng)態(tài)切換JAVA8或JAVA11的方法
這篇文章主要給大家介紹了關(guān)于Windows同時(shí)安裝兩個(gè)版本JDK并實(shí)現(xiàn)動(dòng)態(tài)切換JAVA8或JAVA11的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-11-11Spring Security自定義登錄原理及實(shí)現(xiàn)詳解
這篇文章主要介紹了Spring Security自定義登錄原理及實(shí)現(xiàn)詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09Java8 新特性Lambda表達(dá)式實(shí)例詳解
這篇文章主要介紹了Java8 新特性Lambda表達(dá)式實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-03-03Java使用aspose實(shí)現(xiàn)pdf轉(zhuǎn)word
Aspose是一套強(qiáng)大的文檔處理工具,被超過80%的財(cái)富100強(qiáng)公司信賴,用于在應(yīng)用程序中創(chuàng)建、編輯、導(dǎo)出和轉(zhuǎn)換100多種文件格式,本文將給大家介紹Java使用aspose實(shí)現(xiàn)pdf轉(zhuǎn)word的操作方法,需要的朋友可以參考下2025-02-02關(guān)于intellij idea打開就閃退或關(guān)閉詳細(xì)解決辦法
這篇文章主要介紹了關(guān)于intellij idea打開就閃退或關(guān)閉詳細(xì)解決辦法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03