這個就看代碼,哈哈哈哈哈 須要用到的jar包是:前端
<dependency> <groupId>commons-net</groupId> <artifactId>commons-net</artifactId> <version>3.3</version> </dependency>
一:定義咱們可能會返回的狀態值。兩個枚舉類 一個異常類java
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; //全新下載文件失敗 }
//用於記錄建立時候的異常apache
public class CreateException extends Exception{ private static Logger log = LoggerFactory.getLogger(CreateException.class); private static final long serialVersionUID = 1L; private Integer errCode; private String errMessage; public CreateException(Throwable cause, Integer errCode, String errMessage) { super(cause); this.errCode = errCode; this.errMessage = errMessage; } public CreateException(Integer errCode, String errMessage) { this.errCode = errCode; this.errMessage = errMessage; } public CreateException(Integer errCode, UploadStatus uploadStatus) { this.errCode = errCode; this.errMessage = uploadStatus.toString(); } public Integer getErrCode() { return errCode; } public String getErrMessage() { return errMessage; } }
二:建立鏈接ftp服務器的類瀏覽器
package com.utils.study.ftpCenter; import com.coocaa.core.generation.service.CreateBeanService; 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.FTPReply; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.PrintWriter; /** * Created by yugaofeng on 2017/9/5. */ public class ContinueFTP { private static Logger logger = LoggerFactory.getLogger(CreateBeanService.class); //定義一個客戶端 private static FTPClient ftpClient ; //單例模式 public FTPClient getFtpClient(){ if(ftpClient == null){ ftpClient = new FTPClient(); } return ftpClient; } public ContinueFTP(){ getFtpClient().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 { getFtpClient().connect(hostname, port); if (FTPReply.isPositiveCompletion(getFtpClient().getReplyCode())) { if (getFtpClient().login(username, password)) { getFtpClient().enterLocalPassiveMode(); getFtpClient().setFileType(FTP.BINARY_FILE_TYPE); return true; } } disconnect(); return false; } /** * 斷開與服務器的鏈接 * @throws IOException */ public void disconnect() throws IOException { if (getFtpClient().isConnected()) { getFtpClient().disconnect(); System.out.println("ftp is disconnect!"); } } }
這個時候 先測試一下 你能不能鏈接到服務器服務器
public static void main(String[] args) throws IOException { ContinueFTP ftp = new ContinueFTP(); System.out.println("<<<<<<<<<<<<<<<<<1"+ftp.getFtpClient().isConnected()); ftp.connect("172.20.139.217", 21, "ftp01", "ftp111"); System.out.println("<<<<<<<<<<<<<<<<<3"+ftp.getFtpClient().isConnected()); ftp.getFtpClient().disconnect(); }
表示鏈接成功多線程
三:實現文件上傳 和斷點續傳app
因爲設置了觀察者 在觀察當前上傳的進度變化 ,本次代碼中 沒有添加觀察者模式的代碼,因此這個地方 可能須要先註釋掉觀察者dom
package com.utils.study.ftpCenter; import com.utils.study.CreateException; import com.utils.study.enums.UploadStatus; import com.utils.study.observerModel.FileObserverAble; import com.utils.study.observerModel.FilePercentObserver; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPFile; import java.io.*; /** * Created by yugaofeng on 2017/9/5. */ public class FileOperateByFtp { private FTPClient ftpClient; FileObserverAble fileObserverAble; public FileOperateByFtp(FTPClient ftpClient) { //添加觀察者對象 fileObserverAble = new FileObserverAble(); FilePercentObserver filePercentObserver = new FilePercentObserver(fileObserverAble); this.ftpClient = ftpClient; } /** * 上傳文件到FTP服務器,支持斷點續傳 並返回上傳文件進度 * @param local 本地文件名稱,絕對路徑 * @param remote 遠程文件路徑,使用/home/directory1/subdirectory/file.ext * 按照Linux上的路徑指定方式,支持多級目錄嵌套,支持遞歸建立不存在的目錄結構 * @return 上傳結果 * @throws IOException */ public UploadStatus upload(String local, String remote) throws Exception{ try { if (!ftpClient.isConnected()) { throw new CreateException(-1, "遠程服務器相應目錄建立失敗"); } UploadStatus result; // 對遠程目錄的處理 並返回文件的名稱 String remoteFileName = createDirectory(remote, ftpClient); // 檢查遠程是否存在文件 FTPFile[] files = ftpClient.listFiles(remoteFileName); File localFile = new File(local); if(localFile.length() <=0){ throw new CreateException(-1,"本地文件不存在"); } if (files.length == 1) { //判斷文件是否存在 long remoteSize = files[0].getSize(); long localSize = localFile.length(); if(remoteSize==localSize){ return UploadStatus.File_Exits; }else if(remoteSize > localSize){ return UploadStatus.Remote_Bigger_Local; } result = this.writeByUnit(remoteFileName,localFile,ftpClient,remoteSize,localFile.length()); } else { result = this.writeByUnit(remoteFileName,localFile,ftpClient,0,localFile.length()); } return result; }catch (CreateException e){ throw e; }finally { //上傳完成以後 切回到根目錄 ftpClient.changeWorkingDirectory("/"); } } /** * 判斷目錄 * @param remoteFilePath 遠程服務器上面的 文件目錄 * @param ftpClient ftp客戶端 * @return * @throws Exception */ private String createDirectory(String remoteFilePath,FTPClient ftpClient) throws Exception { if(ftpClient == null){ throw new CreateException(-1,"FTP客戶端爲空,請先鏈接到客戶端"); } String fileName = remoteFilePath; if(remoteFilePath.contains("/")){ fileName = remoteFilePath.substring(remoteFilePath.lastIndexOf("/") + 1); String directory = remoteFilePath.substring(0, remoteFilePath.lastIndexOf("/") + 1); if(directory.startsWith("/")){ directory = directory.substring(1); } while (true){ if(!directory.contains("/")){ break; } String subDirectory = directory.substring(0, directory.indexOf("/")); directory = directory.substring(directory.indexOf("/")+1); if (!ftpClient.changeWorkingDirectory(subDirectory)) { if (ftpClient.makeDirectory(subDirectory)) { ftpClient.changeWorkingDirectory(subDirectory); } else { throw new CreateException(-1,"建立目錄失敗"); } } } } return fileName; } /** * 上傳文件到服務器,新上傳和斷點續傳 * @param remoteFile 遠程文件名,在上傳以前已經將服務器工做目錄作了改變 * @param localFile 本地文件File句柄,絕對路徑 * @param ftpClient FTPClient引用 beginSize是指文件長傳開始指針位置 endSize是結束的位置 爲多線程上傳下載提供接口 不過該方法還須要修改 * @return * @throws IOException */ private UploadStatus writeByUnit(String remoteFile,File localFile,FTPClient ftpClient,long beginSize,long endSize) throws Exception { long localSize = localFile.length(); if(endSize > localSize){ endSize = localSize; } if(beginSize < 0){ beginSize = 0; } //等待寫入的文件大小 long writeSize = endSize - beginSize; if(writeSize <= 0){ throw new CreateException(1,"文件指針參數出錯"); } //獲取百分單位是 1-100 RandomAccessFile raf = new RandomAccessFile(localFile,"r"); OutputStream out = ftpClient.appendFileStream(new String(remoteFile.getBytes("GBK"),"iso-8859-1")); //把文件指針移動到 開始位置 ftpClient.setRestartOffset(beginSize); raf.seek(beginSize); //定義最小移動單位是 1024字節 也就是1kb byte[] bytes = new byte[1024]; int c; double finishSize = 0; double finishPercent = 0; //存在一個bug 當分佈移動的時候 可能會出現下載重複的問題 後期須要修改 while ((c = raf.read(bytes)) != -1) { out.write(bytes, 0, c); finishSize += c; if(finishSize > writeSize){ finishPercent = 1; //System.out.println(">>>>>完成進度:" + finishPercent); fileObserverAble.setKeyValue(localFile.getName(),finishPercent,"upload"); break; } if ((finishSize / writeSize) - finishPercent > 0.01) { finishPercent = finishSize / writeSize; //System.out.println(">>>>>完成進度:" + finishPercent); fileObserverAble.setKeyValue(localFile.getName(),finishPercent,"upload"); } } out.flush(); raf.close(); out.close(); boolean result =ftpClient.completePendingCommand(); return result?UploadStatus.Upload_From_Break_Success:UploadStatus.Upload_From_Break_Failed; } /** * 從FTP服務器上下載文件 * @param remote 遠程文件路徑 * @param local 本地文件路徑 * @return 是否成功 * @throws IOException */ public boolean download(String remote,String local) throws Exception{ FTPFile[] files = ftpClient.listFiles(remote); if(files == null || files.length < 0){ throw new CreateException(-1,"遠程文件不存在"); } if(files.length != 1){ throw new CreateException(-1,"遠程文件不惟一"); } File localFile = new File(local); if(localFile.exists()){ long localBeginSize = localFile.length(); if(localBeginSize == files[0].getSize()){ throw new CreateException(-1,"文件已經存在"); }else if(localBeginSize > files[0].getSize()){ throw new CreateException(-1,"下載文件出錯"); } return downloadByUnit(remote,local,localBeginSize,files[0].getSize()); }else { return downloadByUnit(remote,local,0,files[0].getSize()); } } private Boolean downloadByUnit(String remote,String local,long beginSize,long endSize) throws Exception { File localFile = new File(local); long waitSize = endSize - beginSize; //進行斷點續傳,並記錄狀態 FileOutputStream out = new FileOutputStream(localFile,true); //把文件指針移動到 開始位置 ftpClient.setRestartOffset(beginSize); InputStream in = ftpClient.retrieveFileStream(new String(remote.getBytes("GBK"),"iso-8859-1")); byte[] bytes = new byte[1024]; int c; double finishSize =0; double finishPercent = 0; while((c = in.read(bytes))!= -1){ out.write(bytes,0,c); finishSize += c; if(finishSize > waitSize){ //System.out.println(">>>>>完成進度:" + 1); fileObserverAble.setKeyValue(localFile.getName(),1,"download"); } if ((finishSize / waitSize) - finishPercent > 0.01) { finishPercent = finishSize / waitSize; //System.out.println(">>>>>完成進度:" + finishPercent); fileObserverAble.setKeyValue(localFile.getName(),finishPercent,"download"); } } in.close(); out.close(); return ftpClient.completePendingCommand(); } }
測試上傳 並在控制檯打印出 上傳百分百分佈式
public static void main(String[] args) { ContinueFTP ftp = new ContinueFTP(); try { ftp.connect("172.20.139.217", 21, "ftp01", "ftp111"); FileOperateByFtp fileOperateByFtp = new FileOperateByFtp(ftp.getFtpClient()); fileOperateByFtp.upload("F:\\upload7.temp","/upload2/f3/upload7.temp"); fileOperateByFtp.upload("F:\\upload6.temp","/upload2/f3/upload6.temp"); /* fileOperateByFtp.download("/upload2/f3/upload7.temp","F:\\upload6.temp"); fileOperateByFtp.download("//upload2/f3/upload6.temp","F:\\upload7.temp");*/ if(ftp.getFtpClient() != null){ ftp.getFtpClient().disconnect(); } } catch (Exception e) { if(e instanceof CreateException){ System.out.println(((CreateException) e).getErrMessage()); } } }
上傳結果:測試
下載就不作演示
四:總結
ftp文件長傳其實很簡單,,實現斷點續傳也不能
ftp裏面提供了一個 ftpClient.setRestartOffset(beginSize); 方法 實現了文件指針移動的開始位置 爲後面的 分佈式斷點 多點上傳 提供了 基礎 .
另外關於文件顯示進度比例,在這裏實現也不能,但要是與前端進度條進行實時數據交互式不現實的。。。後來經過查閱資料發現有些還頗有道理的。
好比咱們服務器通常也會限制文件上傳的大小,因此通常顯示進度條是在前端作的,經過比較瀏覽器發送出去的數據量 和帶上傳的文件大小 進行比較來顯示 進度條,但這種方法尚未測試成功,後面會進行驗證。