- Jar包:apache的commons-net包;
- 支持斷點續傳
- 支持進度監控(有時出不來,搞不清原因)
相關知識點
- 編碼格式: UTF-8等;
- 文件類型: 包括[BINARY_FILE_TYPE(常用)]和[ASCII_FILE_TYPE]兩種;
- 數據連接模式:一般使用LocalPassiveMode模式,因為大部分客戶端都在防火牆后面;
1. LocalPassiveMode:服務器端打開數據端口,進行數據傳輸;
2. LocalActiveMode:客戶端打開數據端口,進行數據傳輸;
- 系統類型:UNIX/WINDOWS等,默認為Unix
流程
- 步驟1: 創建FTPClient對象,設置ftpClient屬性:如編碼格式、連接超時、文件上傳下載進度監聽器等;
- 步驟2: 使用ftpClient連接遠程server:connect();
- 步驟3: 獲取connect()的返回碼getReplyCode(),判斷是否連接成功:isPositiveCompletion();
- 步驟4: 登錄遠程server:login(),並轉到相應目錄,必要時要遞歸創建目錄;
- 步驟5: 設置ftpClient屬性:如緩存大小、文件類型、超時時間、數據連接模式等;
- 步驟6: ftp相關操作:如文件上傳、下載等;
- 步驟7: 斷開連接,釋放資源:logout()/disconnect();
程序
FTP連接和登錄


文件上傳



文件下載


測試程序

完整程序
package com.sssppp.Communication;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.net.io.CopyStreamEvent;
import org.apache.commons.net.io.CopyStreamListener;
/**
* FTP進行文件上傳和下載;
* 支持斷點續傳;
*/
public final class FTPUtil {
private final FTPClient ftp = new FTPClient();
/**
*
* @param hostname
* 如:IP
* @param port
* @param username
* @param password
* @return
* @throws IOException
*/
public boolean connect(String hostname, int port, String username,
String password) throws IOException {
boolean debug = false;
if (debug) {
// 設置將過程中使用到的命令輸出到控制台
this.ftp.addProtocolCommandListener(new PrintCommandListener(
new PrintWriter(System.out), true));
}
//設置系統類型
final FTPClientConfig config = new FTPClientConfig(
FTPClientConfig.SYST_UNIX);
this.ftp.configure(config);
try {
this.ftp.connect(hostname, port);
if (!FTPReply.isPositiveCompletion(this.ftp.getReplyCode())) {
this.ftp.disconnect();
System.err.println("FTP server refused connection.");
return false;
}
} catch (IOException e) {
if (this.ftp.isConnected()) {
try {
this.ftp.disconnect();
} catch (IOException f) {
}
}
System.err.println("Could not connect to server.");
e.printStackTrace();
return false;
}
if (!this.ftp.login(username, password)) {
this.ftp.logout();
System.err.println("Could not login to server.");
return false;
}
return true;
}
public void disconnect() throws IOException {
if (this.ftp.isConnected()) {
try {
this.ftp.logout();
this.ftp.disconnect();
} catch (IOException f) {
}
}
}
/**
*
* @param absSrcFileName
* @param destDir
* @param destFileName
* @throws IOException
*/
public void upLoadByFtp(String absSrcFileName, String destDir,
String destFileName) throws IOException {
// 創建並轉到工作目錄
String absDstDir = this.ftp.printWorkingDirectory() + "/" + destDir;
absDstDir = absDstDir.replaceAll("//", "/");
createDirectory(absDstDir, this.ftp);
// 設置各種屬性
this.ftp.setFileType(FTP.BINARY_FILE_TYPE);
// Use passive mode as default because most of us are behind firewalls these days.
this.ftp.enterLocalPassiveMode();
this.ftp.setControlEncoding("utf-8");
this.ftp.setBufferSize(1024);
// 進度監聽
File srcFile = new File(absSrcFileName);
this.ftp.setCopyStreamListener(new MyCopyStreamListener(srcFile.length()));
FTPFile[] files = this.ftp.listFiles(destFileName);
if (files.length == 1) {// 斷點續傳
long dstFileSize = files[0].getSize();
if (srcFile.length() <= dstFileSize) {// 文件已存在
return;
}
boolean b = uploadFile(destFileName, srcFile, this.ftp, dstFileSize);
if (!b) {// 如果斷點續傳沒有成功,則刪除服務器上文件,重新上傳
if (this.ftp.deleteFile(destFileName)) {
uploadFile(destFileName, srcFile, this.ftp, 0);
}else {
System.err.println("Delete file fail.");
}
}
} else {
uploadFile(destFileName, srcFile, this.ftp, 0);
}
}
/**
*
* @param remoteFileName
* @param localFileName
* @throws IOException
*/
public void downLoadByFtp(String remoteFileName, String localFileName)
throws IOException {
InputStream input = null;
FileOutputStream fos = null;
// 設置各種屬性
this.ftp.setBufferSize(1024);
this.ftp.setDataTimeout(1000 * 10);
this.ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
this.ftp.enterLocalPassiveMode();
// 判斷遠程文件是否存在
FTPFile[] files = this.ftp.listFiles(remoteFileName);
if (files.length != 1) {
System.err.println("Remote file not exist.");
return;
}
//進度監聽
long remoteSize = files[0].getSize();
this.ftp.setCopyStreamListener(new MyCopyStreamListener(remoteSize));
File file = new File(localFileName);
if (file.exists()) {
long localSize = file.length();
if (localSize >= remoteSize) {
return;
}
System.out.println("@@@Break point download.@@@");
fos = new FileOutputStream(file, true);// append模式
this.ftp.setRestartOffset(localSize);
} else {
fos = new FileOutputStream(file); // override模式
}
input = this.ftp.retrieveFileStream(remoteFileName);
byte[] b = new byte[8192];
int n = 0;
while (-1 != (n = input.read(b))) {
if (Thread.currentThread().isInterrupted()) {
break;
}
fos.write(b, 0, n);
}
if (input != null) {
input.close();
}
if (fos != null) {
fos.flush();
fos.close();
}
if (!this.ftp.completePendingCommand()) {
System.err.println("Download file fail.");
this.ftp.logout();
this.ftp.disconnect();
}
}
/**
*
* @param destFileName
* @param srcFile
* @param ftpClient
* @param dstFileSize 文件寫入的起始位置; >0:表示斷點續傳,<=0:表示上傳新文件
* @return
* @throws IOException
*/
private boolean uploadFile(String destFileName, File srcFile,
FTPClient ftpClient, long dstFileSize) throws IOException {
RandomAccessFile input = null;
OutputStream fout = null;
input = new RandomAccessFile(srcFile, "r"); // 只讀模式
if (dstFileSize > 0) {// 斷點續傳
fout = ftpClient.appendFileStream(destFileName);
input.seek(dstFileSize);
ftpClient.setRestartOffset(dstFileSize);
} else {
fout = ftpClient.storeFileStream(destFileName);
}
byte[] b = new byte[8192]; // 緩存大小
int n = 0;
while (-1 != (n = input.read(b))) {
if (Thread.currentThread().isInterrupted()) {
break;
}
fout.write(b, 0, n);
}
if (input != null) {
input.close();
}
if (fout != null) {
fout.flush();
fout.close();
}
if (!ftpClient.completePendingCommand()) {
System.err.println("Upload file fail.");
ftpClient.logout();
ftpClient.disconnect();
return false;
}
return true;
}
/**
* 在FTP服務器上創建並轉到工作目錄
*
* @param relativePath
* 相對工作路徑,不包含文件名:如 dd/11/22/33
* @param ftpClient
* 錄創建是否成功
* @return
* @throws IOException
*/
private boolean createDirectory(String relativePath, FTPClient ftpClient)
throws IOException {
if (!relativePath.startsWith("/")) {
relativePath = "/" + relativePath;
}
String dir = (ftpClient.printWorkingDirectory().equals("/") ? ""
: ftpClient.printWorkingDirectory()) + relativePath;
if (!ftpClient.changeWorkingDirectory(dir)) {
//目錄不存在,則創建各級目錄
for (String subDir : relativePath.split("/")) {
if (!subDir.equals("")) {
String newDir = ftpClient.printWorkingDirectory() + "/"
+ subDir;
ftpClient.mkd(newDir);
if (!ftpClient.changeWorkingDirectory(newDir)) {
return false;
}
}
}
}
return true;
}
/**
* 進度監聽器
*/
private class MyCopyStreamListener implements CopyStreamListener {
private long totalSize = 0;
private long percent = -1; // 進度
/**
* 文件的總大小
* @param totalSize
*/
public MyCopyStreamListener(long totalSize) {
super();
this.totalSize = totalSize;
}
@Override
public void bytesTransferred(CopyStreamEvent event) {
bytesTransferred(event.getTotalBytesTransferred(),
event.getBytesTransferred(), event.getStreamSize());
}
//totalBytesTransferred:當前總共已傳輸字節數;
//bytesTransferred:最近一次傳輸字節數
@Override
public void bytesTransferred(long totalBytesTransferred,
int bytesTransferred, long streamSize) {
if (percent >= totalBytesTransferred * 100 / totalSize) {
return;
}
percent = totalBytesTransferred * 100 / totalSize;
System.out.println("Completed " + totalBytesTransferred + "("
+ percent + "%) out of " + totalSize + ".");
}
}
public static void main(String[] args) throws IOException {
String hostname = "10.180.137.241";
String username = "xxx";
String password = "xxx";
int port = 21;
FTPUtil ftp = new FTPUtil();
//上傳文件
String absSrcFileName = "C:\\tmp\\m2eclipse1.zip";
String destDir = "ww/11/22/33";
String destFileName = "m2eclipse1.zip";
ftp.connect(hostname, port, username, password);
ftp.upLoadByFtp(absSrcFileName, destDir, destFileName);
ftp.disconnect();
// 下載文件
String localFileName = "C:\\tmp\\m2eclipse-download3333.zip";
String remoteFileName = "/ww/11/22/33/m2eclipse.zip";
ftp.connect(hostname, port, username, password);
ftp.downLoadByFtp(remoteFileName, localFileName);
ftp.disconnect();
}
}
參考鏈接
https://my.oschina.net/csmw00/blog/676049 (可參考如何實現進度監控)