FTP簡介
FTP(File Transfer Protocol)是因特網中使用最廣泛的文件傳輸協議。FTP協議是應用層協議,它是基於TCP協議的。FTP是一個客戶/服務器系統。
FTP的兩種傳輸模式(主動模式和被動模式)
關於”主動”還是”被動”都是針對於FTP服務器來說的,是選擇主動模式還是被動模式是由客戶端決定的。
主動模式
ftp主動模式過程大致如下:
1、客戶端隨機選取一個大於1024的非特權端口與FTP服務器的21號端口進行控制連接,由於FTP是基於TCP的,這個過程要經歷三次握手。連接成功後,以後客戶端與服務器之間的控制命令如上傳、下載、切換路徑等命令都是通過控制連接來傳輸的。
2、當需要進行數據連接且客戶端選擇發起的是主動模式的連接時,客戶端會隨機選擇一個端口portB,通過命令通道發送到FTP服務器,並等待FTP服務器主動發起FTP數據連接。
3、FTP服務器收到了客戶端的請求,並主動通過20號端口向客戶端的portB發起數據連接,這個過程也是有三次握手的。
這樣在客戶機與FTP服務器就建立了兩個連接,一個控制連接用於傳輸命令,一個數據連接用於傳輸數據。
ftp連接在客戶端沒有發起數據請求時是不會馬上建立數據連接的。FTP上控制連接端口號是21,數據連接端口號是20。
被動模式
被動模式解析:
1、建立控制連接、同主動模式。
2、當需要數據連接時,客戶端向ftp服務器發起被動模式請求,等待服務器的迴應。
3、服務器接收到請求並開啓一個監聽的端口號pasvPort,ftp服務器通過命令通過告知客戶機自己的被動連接端口pasvPort,等待客戶端發起連接。
4、客戶機向隨機選取一個portB向ftp服務器的pasvPort端口發起數據連接
可以看到被動模式的FTP數據連接是由客戶機主動發起的。
在Java項目中如何使用FTP服務
1、使用的是FTPClient,需要在maven中添加依賴:
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>1.4.1</version>
</dependency>
2、新建了一個FTPUtil工具類,用來管理FTP的連接、上傳和下載。
public class FtpUtil {
public static final String ftpHost = "111.231.250.43";
public static final String ftpUserName = "lcl";
public static final String ftpPassword = "123";
public static final int ftpPort = 21;
public static final String ftpPath = "appdata/service/";
public static final String ftpPathClient = "appdata/client/";
public static final String uploadPath = System.getProperty("user.dir");
/**
* 獲取FTPClient對象
*
* @param ftpHost FTP主機服務器
* @param ftpPassword FTP 登錄密碼
* @param ftpUserName FTP登錄用戶名
* @param ftpPort FTP端口 默認爲21
* @return
*/
public static FTPClient getFTPClient(String ftpHost, String ftpUserName,
String ftpPassword, int ftpPort) {
FTPClient ftpClient = new FTPClient();
try {
ftpClient = new FTPClient();
ftpClient.connect(ftpHost, ftpPort);// 連接FTP服務器
ftpClient.login(ftpUserName, ftpPassword);// 登陸FTP服務器
if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
System.out.println("未連接到FTP,用戶名或密碼錯誤。");
ftpClient.disconnect();
return null;
} else {
System.out.println("FTP連接成功。");
}
} catch (SocketException e) {
e.printStackTrace();
System.out.println("FTP的IP地址可能錯誤,請正確配置。");
return null;
} catch (IOException e) {
e.printStackTrace();
System.out.println("FTP的端口錯誤,請正確配置。");
return null;
}
return ftpClient;
}
/*
* 從FTP服務器下載文件
*
* @param ftpHost FTP IP地址
* @param ftpUserName FTP 用戶名
* @param ftpPassword FTP用戶名密碼
* @param ftpPort FTP端口
* @param ftpPath FTP服務器中文件所在路徑 格式: appdata/service/年月/
* @param localPath 下載到本地的位置 格式:xx/upload/
* @param fileName 文件名稱 如 app_20180803.zip
*/
public static boolean downloadFtpFile(String ftpHost, String ftpUserName,
String ftpPassword, int ftpPort, String ftpPath, String localPath,String fileName) {
FTPClient ftpClient = null;
try {
ftpClient = getFTPClient(ftpHost, ftpUserName, ftpPassword, ftpPort);
if(ftpClient == null){
//ftp連接不上返回false
return false;
}
ftpClient.setControlEncoding("UTF-8"); // 中文支持
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
//客戶機進入ftp的被動模式,主動向FTP服務器發起數據連接
ftpClient.enterLocalPassiveMode();
//切換到服務器的指定路徑下
ftpClient.changeWorkingDirectory(ftpPath);
//創建要下載的文件
File localFile = new File(localPath+fileName);
if(!localFile.exists()){
localFile.createNewFile();
}
OutputStream os = new FileOutputStream(localFile);
//retrieveFile方法將當前路徑下名爲fileName的文件放到os輸出流中,即進行文件下載
boolean flag = ftpClient.retrieveFile(fileName, os);
os.flush();
os.close();
ftpClient.logout();
if(!flag){
return false;
}
return true;
} catch (FileNotFoundException e) {
System.out.println("沒有找到" + ftpPath + "文件");
e.printStackTrace();
return false;
} catch (SocketException e) {
System.out.println("連接FTP失敗.");
e.printStackTrace();
return false;
} catch (IOException e) {
e.printStackTrace();
System.out.println("文件讀取錯誤。");
e.printStackTrace();
return false;
}
}
/**
* Description: 向FTP服務器上傳文件
* @param ftpHost FTP服務器hostname
* @param ftpUserName 賬號
* @param ftpPassword 密碼
* @param ftpPort 端口
* @param ftpPath FTP服務器中文件所在路徑 格式: appdata/client/
* @param fileName ftp文件名稱
* @param input 文件流
* @return 成功返回true,否則返回false
*/
public static boolean uploadFile(String ftpHost, String ftpUserName,
String ftpPassword, int ftpPort, String ftpPath,
String fileName,InputStream input) {
boolean success = false;
FTPClient ftpClient = null;
try {
int reply;
ftpClient = getFTPClient(ftpHost, ftpUserName, ftpPassword, ftpPort);
reply = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftpClient.disconnect();
return success;
}
ftpClient.setControlEncoding("UTF-8"); // 中文支持
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
ftpClient.enterLocalPassiveMode();
ftpClient.changeWorkingDirectory(ftpPath);
String dirName = DateHelper.getYearAndMonth();
//向服務器上查詢文件是否存在,若不存在則創建文件
ftpClient.makeDirectory(dirName);
//切換到創建的文件夾下
ftpClient.changeWorkingDirectory(dirName);
//在ftp服務器上新建一個叫fileName發文件,並將input輸入流中的內容寫入fileName
ftpClient.storeFile(fileName, input);
input.close();
ftpClient.logout();
success = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ftpClient.isConnected()) {
try {
ftpClient.disconnect();
} catch (IOException ioe) {
}
}
}
return success;
}
}