1. 程式人生 > >java實現ftp斷點續傳

java實現ftp斷點續傳

利用org.apache.commons.net.ftp包實現一個簡單的ftp客戶端實用類。主要實現一下功能

1.支援上傳下載。支援斷點續傳

2.支援進度彙報

3.支援對於中文目錄及中文檔案建立的支援。

 列舉類UploadStatus程式碼

public enum UploadStatus {
Create_Directory_Fail,//遠端伺服器相應目錄建立失敗
Create_Directory_Success,//遠端伺服器闖將目錄成功
Upload_New_File_Success,//上傳新檔案成功
Upload_New_File_Failed,//上傳新檔案失敗
File_Exits, //檔案已經存在
Remote_Bigger_Local,//遠端檔案大於本地檔案
Upload_From_Break_Success,//斷點續傳成功
Upload_From_Break_Failed,//斷點續傳失敗
Delete_Remote_Faild;//刪除遠端檔案失敗
}

public enum DownloadStatus {
Remote_File_Noexist,//遠端檔案不存在
Local_Bigger_Remote,//本地檔案大於遠端檔案
Download_From_Break_Success,//斷點下載檔案成功
Download_From_Break_Failed,//斷點下載檔案失敗
Download_New_Success,//全新下載檔案成功
Download_New_Failed;//全新下載檔案失敗
}

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 open.mis.data.DownloadStatus;
import open.mis.data.UploadStatus;


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.FTPFile;
import org.apache.commons.net.ftp.FTPReply;


/**
 * 支援斷點續傳的FTP實用類
 * @author BenZhou
 * @version 0.1 實現基本斷點上傳下載
 * @version 0.2 實現上傳下載進度彙報
 * @version 0.3 實現中文目錄建立及中文檔案建立,新增對於中文的支援
 */
public class ContinueFTP {
public FTPClient ftpClient = new FTPClient();

public ContinueFTP(){
//設定將過程中使用到的命令輸出到控制檯
this.ftpClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
}

/**
* 連線到FTP伺服器
* @param hostname 主機名
* @param port 埠
* @param username 使用者名稱
* @param password 密碼
* @return 是否連線成功
* @throws IOException
*/
public boolean connect(String hostname,int port,String username,String password) throws IOException{
ftpClient.connect(hostname, port);
ftpClient.setControlEncoding("GBK");
if(FTPReply.isPositiveCompletion(ftpClient.getReplyCode())){
if(ftpClient.login(username, password)){
return true;
}
}
disconnect();
return false;
}

/**
* 從FTP伺服器上下載檔案,支援斷點續傳,上傳百分比彙報
* @param remote 遠端檔案路徑
* @param local 本地檔案路徑
* @return 上傳的狀態
* @throws IOException
*/
public DownloadStatus download(String remote,String local) throws IOException{
//設定被動模式
ftpClient.enterLocalPassiveMode();
//設定以二進位制方式傳輸
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
DownloadStatus result;

//檢查遠端檔案是否存在
FTPFile[] files = ftpClient.listFiles(new String(remote.getBytes("GBK"),"iso-8859-1"));
if(files.length != 1){
System.out.println("遠端檔案不存在");
return DownloadStatus.Remote_File_Noexist;
}

long lRemoteSize = files[0].getSize();
File f = new File(local);
//本地存在檔案,進行斷點下載
if(f.exists()){
long localSize = f.length();
//判斷本地檔案大小是否大於遠端檔案大小
if(localSize >= lRemoteSize){
System.out.println("本地檔案大於遠端檔案,下載中止");
return DownloadStatus.Local_Bigger_Remote;
}

//進行斷點續傳,並記錄狀態
FileOutputStream out = new FileOutputStream(f,true);
ftpClient.setRestartOffset(localSize);
InputStream in = ftpClient.retrieveFileStream(new String(remote.getBytes("GBK"),"iso-8859-1"));
byte[] bytes = new byte[1024];
long step = lRemoteSize /100;
long process=localSize /step;
int c;
while((c = in.read(bytes))!= -1){
out.write(bytes,0,c);
localSize+=c;
long nowProcess = localSize /step;
if(nowProcess > process){
process = nowProcess;
if(process % 10 == 0)
System.out.println("下載進度:"+process);
//TODO 更新檔案下載進度,值存放在process變數中
}
}
in.close();
out.close();
boolean isDo = ftpClient.completePendingCommand();
if(isDo){
result = DownloadStatus.Download_From_Break_Success;
}else {
result = DownloadStatus.Download_From_Break_Failed;
}
}else {
OutputStream out = new FileOutputStream(f);
InputStream in= ftpClient.retrieveFileStream(new String(remote.getBytes("GBK"),"iso-8859-1"));
byte[] bytes = new byte[1024];
long step = lRemoteSize /100;
long process=0;
long localSize = 0L;
int c;
while((c = in.read(bytes))!= -1){
out.write(bytes, 0, c);
localSize+=c;
long nowProcess = localSize /step;
if(nowProcess > process){
process = nowProcess;
if(process % 10 == 0)
System.out.println("下載進度:"+process);
//TODO 更新檔案下載進度,值存放在process變數中
}
}
in.close();
out.close();
boolean upNewStatus = ftpClient.completePendingCommand();
if(upNewStatus){
result = DownloadStatus.Download_New_Success;
}else {
result = DownloadStatus.Download_New_Failed;
}
}
return result;
}

/**
* 上傳檔案到FTP伺服器,支援斷點續傳
* @param local 本地檔名稱,絕對路徑
* @param remote 遠端檔案路徑,使用/home/directory1/subdirectory/file.ext 按照Linux上的路徑指定方式,支援多級目錄巢狀,支援遞迴建立不存在的目錄結構
* @return 上傳結果
* @throws IOException
*/
public UploadStatus upload(String local,String remote) throws IOException{
//設定PassiveMode傳輸
ftpClient.enterLocalPassiveMode();
//設定以二進位制流的方式傳輸
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
ftpClient.setControlEncoding("GBK");
UploadStatus result;
//對遠端目錄的處理
String remoteFileName = remote;
if(remote.contains("/")){
remoteFileName = remote.substring(remote.lastIndexOf("/")+1);
//建立伺服器遠端目錄結構,建立失敗直接返回
if(CreateDirecroty(remote, ftpClient)==UploadStatus.Create_Directory_Fail){
return UploadStatus.Create_Directory_Fail;
}
}

//檢查遠端是否存在檔案
FTPFile[] files = ftpClient.listFiles(new String(remoteFileName.getBytes("GBK"),"iso-8859-1"));
if(files.length == 1){
long remoteSize = files[0].getSize();
File f = new File(local);
long localSize = f.length();
if(remoteSize==localSize){
return UploadStatus.File_Exits;
}else if(remoteSize > localSize){
return UploadStatus.Remote_Bigger_Local;
}

//嘗試移動檔案內讀取指標,實現斷點續傳
result = uploadFile(remoteFileName, f, ftpClient, remoteSize);

//如果斷點續傳沒有成功,則刪除伺服器上檔案,重新上傳
if(result == UploadStatus.Upload_From_Break_Failed){
if(!ftpClient.deleteFile(remoteFileName)){
return UploadStatus.Delete_Remote_Faild;
}
result = uploadFile(remoteFileName, f, ftpClient, 0);
}
}else {
result = uploadFile(remoteFileName, new File(local), ftpClient, 0);
}
return result;
}
/**
* 斷開與遠端伺服器的連線
* @throws IOException
*/
public void disconnect() throws IOException{
if(ftpClient.isConnected()){
ftpClient.disconnect();
}
}

/**
* 遞迴建立遠端伺服器目錄
* @param remote 遠端伺服器檔案絕對路徑
* @param ftpClient FTPClient物件
* @return 目錄建立是否成功
* @throws IOException
*/
public UploadStatus CreateDirecroty(String remote,FTPClient ftpClient) throws IOException{
UploadStatus status = UploadStatus.Create_Directory_Success;
String directory = remote.substring(0,remote.lastIndexOf("/")+1);
if(!directory.equalsIgnoreCase("/")&&!ftpClient.changeWorkingDirectory(new String(directory.getBytes("GBK"),"iso-8859-1"))){
//如果遠端目錄不存在,則遞迴建立遠端伺服器目錄
int start=0;
int end = 0;
if(directory.startsWith("/")){
start = 1;
}else{
start = 0;
}
end = directory.indexOf("/",start);
while(true){
String subDirectory = new String(remote.substring(start,end).getBytes("GBK"),"iso-8859-1");
if(!ftpClient.changeWorkingDirectory(subDirectory)){
if(ftpClient.makeDirectory(subDirectory)){
ftpClient.changeWorkingDirectory(subDirectory);
}else {
System.out.println("建立目錄失敗");
return UploadStatus.Create_Directory_Fail;
}
}

start = end + 1;
end = directory.indexOf("/",start);

//檢查所有目錄是否建立完畢
if(end <= start){
break;
}
}
}
return status;
}

/**
* 上傳檔案到伺服器,新上傳和斷點續傳
* @param remoteFile 遠端檔名,在上傳之前已經將伺服器工作目錄做了改變
* @param localFile 本地檔案File控制代碼,絕對路徑
* @param processStep 需要顯示的處理進度步進值
* @param ftpClient FTPClient引用
* @return
* @throws IOException
*/
public UploadStatus uploadFile(String remoteFile,File localFile,FTPClient ftpClient,long remoteSize) throws IOException{
UploadStatus status;
//顯示進度的上傳
long step = localFile.length() / 100;
long process = 0;
long localreadbytes = 0L;
RandomAccessFile raf = new RandomAccessFile(localFile,"r");
OutputStream out = ftpClient.appendFileStream(new String(remoteFile.getBytes("GBK"),"iso-8859-1"));
//斷點續傳
if(remoteSize>0){
ftpClient.setRestartOffset(remoteSize);
process = remoteSize /step;
raf.seek(remoteSize);
localreadbytes = remoteSize;
}
byte[] bytes = new byte[1024];
int c;
while((c = raf.read(bytes))!= -1){
out.write(bytes,0,c);
localreadbytes+=c;
if(localreadbytes / step != process){
process = localreadbytes / step;
System.out.println("上傳進度:" + process);
//TODO 彙報上傳狀態
}
}
out.flush();
raf.close();
out.close();
boolean result =ftpClient.completePendingCommand();
if(remoteSize > 0){
status = result?UploadStatus.Upload_From_Break_Success:UploadStatus.Upload_From_Break_Failed;
}else {
status = result?UploadStatus.Upload_New_File_Success:UploadStatus.Upload_New_File_Failed;
}
return status;
}

public static void main(String[] args) {
ContinueFTP myFtp = new ContinueFTP();
try {
myFtp.connect("127.0.0.1", 21, "nid", "123");
//myFtp.ftpClient.makeDirectory(new String("歌曲".getBytes("GBK"),"iso-8859-1"));
//myFtp.ftpClient.changeWorkingDirectory(new String("歌曲".getBytes("GBK"),"iso-8859-1"));
//myFtp.ftpClient.makeDirectory(new String("愛你等於愛自己".getBytes("GBK"),"iso-8859-1"));
//System.out.println(myFtp.upload("E:\\yw.flv", "/yw.flv",5));
//System.out.println(myFtp.upload("E:\\愛你等於愛自己.mp4","/愛你等於愛自己.mp4"));
System.out.println(myFtp.download("/愛你等於愛自己.mp4", "E:\\愛你等於愛自己.mp4"));
myFtp.disconnect();
} catch (IOException e) {
System.out.println("連線FTP出錯:"+e.getMessage());
}
}
}