點選上方 "程式員小樂"關注, 星标或置頂一起成長
每天淩晨00點00分, 第一時間與你相約
每日英文
For something,we can't understand when we are young but by the time we understand,we are no longer young.
有些事情,當我們年輕的時候無法懂得,當我們懂得的時候已不再年輕。
每日掏心話
人無所舍,必無所成。心無所依,必無所獲。自己的路隻有自己去走,自己的心還須自己去度。
來自:宇的季節 | 責編:樂樂
連結:cnblogs.com/chenkeyu/p/8001624.html
程式員小樂(ID:study_tech)第 962 次推文 圖源:百度
往日回顧:40+ 張最全 Linux / C / C++ 思維導圖,收藏!
正文
前言
最近在項目中需要和ftp伺服器進行互動,在網上找了一下關于ftp上傳下載下傳的工具類,大緻有兩種。
- 第一種是單例模式的類。
- 第二種是另外定義一個Service,直接通過Service來實作ftp的上傳下載下傳删除。
這兩種感覺都有利弊。
- 第一種實作了代碼複用,但是配置資訊全需要寫在類中,維護比較複雜。
- 第二種如果是spring架構,可以通過propertis檔案,動态的注入配置資訊,但是又不能代碼複用。
是以我打算自己實作一個工具類,來把上面的兩種優點進行整合。順便把一些上傳過程中一些常見的問題也給解決了。
因為我使用的是spring架構,如果把工具類聲明為bean給spring管理,他預設就是單例的,是以不需要我再實作單例。并且因為是bean,是以我可以把properties檔案的屬性注入bean的屬性中,實作解耦,下面是具體代碼:
關注公衆号程式員小樂回複關鍵字“offer”擷取算法面試題和答案。
package com.cky.util;
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 org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//使用spring自動生成單例對象,
//@Component
public class FtpUtil {
//通過properties檔案自動注入
@Value("${ftp.host}")
private String host; //ftp伺服器ip
@Value("${ftp.port}")
private int port; //ftp伺服器端口
@Value("${ftp.username}")
private String username;//使用者名
@Value("${ftp.password}")
private String password;//密碼
@Value("${ftp.basePath}")
private String basePath;//存放檔案的基本路徑
//測試的時候把這個構造函數打開,設定你的初始值,然後在代碼後面的main方法運作測試
public FtpUtil() {
//System.out.println(this.toString());
host="192.168.100.77";
port=21;
username="ftpuser";
password="ftp54321";
basePath="/home/ftpuser/www/images";
}
/**
*
* @param path 上傳檔案存放在伺服器的路徑
* @param filename 上傳檔案名
* @param input 輸入流
* @return
*/
public boolean fileUpload(String path,String filename,InputStream input) {
FTPClient ftp=new FTPClient();
try {
ftp.connect(host, port);
ftp.login(username, password);
//設定檔案編碼格式
ftp.setControlEncoding("UTF-8");
//ftp通信有兩種模式
//PORT(主動模式)用戶端開通一個新端口(>1024)并通過這個端口發送指令或傳輸資料,期間服務端隻使用他開通的一個端口,例如21
//PASV(被動模式)用戶端向服務端發送一個PASV指令,服務端開啟一個新端口(>1024),并使用這個端口與用戶端的21端口傳輸資料
//由于用戶端不可控,防火牆等原因,是以需要由服務端開啟端口,需要設定被動模式
ftp.enterLocalPassiveMode();
//設定傳輸方式為流方式
ftp.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
//擷取狀态碼,判斷是否連接配接成功
if(!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
throw new RuntimeException("FTP伺服器拒絕連接配接");
}
//轉到上傳檔案的根目錄
if(!ftp.changeWorkingDirectory(basePath)) {
throw new RuntimeException("根目錄不存在,需要建立");
}
//判斷是否存在目錄
if(!ftp.changeWorkingDirectory(path)) {
String[] dirs=path.split("/");
//建立目錄
for (String dir : dirs) {
if(null==dir||"".equals(dir)) continue;
//判斷是否存在目錄
if(!ftp.changeWorkingDirectory(dir)) {
//不存在則建立
if(!ftp.makeDirectory(dir)) {
throw new RuntimeException("子目錄建立失敗");
}
//進入新建立的目錄
ftp.changeWorkingDirectory(dir);
}
}
//設定上傳檔案的類型為二進制類型
ftp.setFileType(FTP.BINARY_FILE_TYPE);
//上傳檔案
if(!ftp.storeFile(filename, input)) {
return false;
}
input.close();
ftp.logout();
return true;
}
} catch (Exception e) {
throw new RuntimeException(e);
}finally {
if(ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
return false;
}
/**
*
* @param filename 檔案名,注意!此處檔案名為加路徑檔案名,如:/2015/06/04/aa.jpg
* @param localPath 存放到本地第位址
* @return
*/
public boolean downloadFile(String filename,String localPath) {
FTPClient ftp=new FTPClient();
try {
ftp.connect(host, port);
ftp.login(username, password);
//設定檔案編碼格式
ftp.setControlEncoding("UTF-8");
//ftp通信有兩種模式
//PORT(主動模式)用戶端開通一個新端口(>1024)并通過這個端口發送指令或傳輸資料,期間服務端隻使用他開通的一個端口,例如21
//PASV(被動模式)用戶端向服務端發送一個PASV指令,服務端開啟一個新端口(>1024),并使用這個端口與用戶端的21端口傳輸資料
//由于用戶端不可控,防火牆等原因,是以需要由服務端開啟端口,需要設定被動模式
ftp.enterLocalPassiveMode();
//設定傳輸方式為流方式
ftp.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
//擷取狀态碼,判斷是否連接配接成功
if(!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
throw new RuntimeException("FTP伺服器拒絕連接配接");
}
int index=filename.lastIndexOf("/");
//擷取檔案的路徑
String path=filename.substring(0, index);
//擷取檔案名
String name=filename.substring(index+1);
//判斷是否存在目錄
if(!ftp.changeWorkingDirectory(basePath+path)) {
throw new RuntimeException("檔案路徑不存在:"+basePath+path);
}
//擷取該目錄所有檔案
FTPFile[] files=ftp.listFiles();
for (FTPFile file : files) {
//判斷是否有目标檔案
//System.out.println("檔案名"+file.getName()+"---"+name);
if(file.getName().equals(name)) {
//System.out.println("找到檔案");
//如果找到,将目标檔案複制到本地
File localFile =new File(localPath+"/"+file.getName());
OutputStream out=new FileOutputStream(localFile);
ftp.retrieveFile(file.getName(), out);
out.close();
}
}
ftp.logout();
return true;
} catch (Exception e) {
throw new RuntimeException(e);
}finally {
if(ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
public boolean deleteFile(String filename) {
FTPClient ftp=new FTPClient();
try {
ftp.connect(host, port);
ftp.login(username, password);
//設定編碼格式
ftp.setControlEncoding("UTF-8");
ftp.enterLocalPassiveMode();
//擷取狀态碼,判斷是否連接配接成功
if(!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
throw new RuntimeException("FTP伺服器拒絕連接配接");
}
int index=filename.lastIndexOf("/");
//擷取檔案的路徑
String path=filename.substring(0, index);
//擷取檔案名
String name=filename.substring(index+1);
//判斷是否存在目錄,不存在則說明檔案存在
if(!ftp.changeWorkingDirectory(basePath+path)) {
return true;
}
if(ftp.deleteFile(name)) {
clearDirectory(ftp, basePath, path);
ftp.logout();
return true;
}
ftp.logout();
return false;
} catch (Exception e) {
throw new RuntimeException(e);
}finally {
if(ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
/**
*
* @param ftp
* @param basePath
* @param path 以path為根,遞歸清除上面所有空的檔案夾,直到出現不為空的檔案夾停止,最多清除到basePath結束
* @throws IOException
*/
private void clearDirectory(FTPClient ftp,String basePath,String path) throws IOException {
//如果路徑長度小于2,說明到頂了
if(path.length()<2) {
return ;
}
//如果目前目錄檔案數目小于1則删除此目錄
if(ftp.listNames(basePath+path).length<1) {
//删除目錄
System.out.println("删除目錄:"+basePath+path);
ftp.removeDirectory(basePath+path);
int index=path.lastIndexOf("/");
//路徑向上一層
path=path.substring(0, index);
//繼續判斷
clearDirectory(ftp, basePath, path);
}
}
//兩個功能其中一個使用的話另一個需要注釋
public static void main(String []args) {
//上傳測試--------------------------------------
/*FileInputStream in;
try {
in=new FileInputStream(new File("C:\\Users\\Administrator\\Desktop\\json.png"));
FtpUtil ftputil=new FtpUtil();
boolean flag=ftputil.fileUpload("/2015/06/04", "va.jpg", in);
System.out.println(flag);
}catch (Exception e) {
e.printStackTrace();
}finally {
}*/
//下載下傳測試--------------------------------------
/*String filename="/2015/06/04/aa.jpg";
String localPath="F:\\";
FtpUtil ftputil=new FtpUtil();
ftputil.downloadFile(filename, localPath);*/
//删除測試--------------------------------------
FtpUtil ftputil=new FtpUtil();
boolean flag=ftputil.deleteFile("/2015/06/04/va.jpg");
System.out.println(flag);
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getBasePath() {
return basePath;
}
public void setBasePath(String basePath) {
this.basePath = basePath;
}
@Override
public String toString() {
return "FtpUtil [host=" + host + ", port=" + port + ", username=" + username + ", password=" + password
+ ", basePath=" + basePath + "]";
}
}
具體使用
第一步:配置spring加載properties檔案
關注公衆号程式員小樂回複關鍵字“Java”擷取Java面試題和答案。
applicationContext.xml
<context:property-placeholder location="classpath:*.properties"/>
ftp.properties
ftp.host=192.168.100.77
ftp.port=21
ftp.username=ftpuser
ftp.password=ftp54321
ftp.basePath=/home/ftpuser/
第二步:将工具類聲明為bean
xml方式
<bean id="ftpUtil" class="com.cky.util.FtpUtil">
<property name="host" value="${ftp.host}"></property>
<property name="port" value="${ftp.port}"></property>
<property name="username" value="${ftp.username}"></property>
<property name="password" value="${ftp.password}"></property>
<property name="basePath" value="${ftp.basePath}"></property>
</bean>
注解方式,元件掃描
<context:component-scan base-package="com.cky.util"></context:component-scan>
第三步:注入使用
@Autowired
private FtpUtil ftpUtil;
FTP常見問題:
1、連接配接失敗
(1)檢查ftp伺服器是否打開
(2)檢查防火牆是否将ftp伺服器端口加入白名單(測試的話也可以直接把防火牆關掉)
2、建立檔案或者上傳檔案失敗:
最常見的就是檔案權限問題造成的,不光是讀寫權限,還有檔案的使用者組。
例如:ftpClient登入的使用者是ftpUser 一般是檔案存放在/home/ftpUser中,一般我們不會直接放在根目錄,而是建立對應檔案夾,這時需要注意,建立檔案夾時需要以ftpUser或者同一組使用者的身份建立,如果你是以root使用者建立的,那麼ftpUser将無權通路!
歡迎在留言區留下你的觀點,一起讨論提高。如果今天的文章讓你有新的啟發,歡迎轉發分享給更多人。歡迎加入程式員小樂加群”或者“阿裡、騰訊、百度、華為、京東最新面試題彙集
如何保證緩存與資料庫的雙寫一緻性?
分布式之分布式事務、分布式鎖、分布式Session
用 GitLab 做 CI/CD 是什麼感覺,太強了!!
關注訂閱号「程式員小樂」,收看更多精彩内容
嘿,你在看嗎?