Apache FTP多執行緒檔案上傳、下載、修改檔名、刪除
阿新 • • 發佈:2019-02-19
此處實現多執行緒對FTP檔案的操作,FTPStatus來自上一篇文章,下附工具程式碼。
package com.scengine.wtms.utils.ftp; 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 java.io.PrintWriter; import java.net.SocketException; import javax.servlet.http.HttpServletResponse; 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; import com.scengine.wtms.utils.Log; public class ThreadFTPUtils implements Runnable { private UserInfo userInfo; private FTPClient ftpClient = new FTPClient(); private FTPType ftpType; public FTPType getFtpType() { return ftpType; } public void setFtpType(FTPType ftpType) { this.ftpType = ftpType; } public static enum FTPType{ UPLOAD(0),DOWNLOAD(1),RENAME(2),DELETE(3); private int type; public int getType() { return type; } public void setType(int type) { this.type = type; } FTPType(int type){ this.type=type; } } /** * 物件構造 設定將過程中使用到的命令輸出到控制檯 */ public ThreadFTPUtils(String ip,int port,String username,String password,String local,String remote,FTPType ftpType) { userInfo=new UserInfo(ip, port, username, password, local, remote); this.ftpType=ftpType; this.ftpClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out))); } /** * * java程式設計中用於連線到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); if (FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) { if (ftpClient.login(username, password)) { return true; } } disconnect(); return false; } /** * 刪除遠端FTP檔案 * * @param remote * 遠端檔案路徑 * @return * @throws IOException */ public FTPStatus delete(String remote) throws IOException { ftpClient.enterLocalPassiveMode(); ftpClient.setFileType(FTP.BINARY_FILE_TYPE); FTPStatus result = null; FTPFile[] files = ftpClient.listFiles(remote); if (files.length == 1) { boolean status = ftpClient.deleteFile(remote); result = status ? FTPStatus.Delete_Remote_Success : FTPStatus.Delete_Remote_Faild; } else { result = FTPStatus.Not_Exist_File; } Log.getLogger(this.getClass()).info("FTP伺服器檔案刪除標識:"+result); return result; } /** * 重新命名遠端FTP檔案 * * @param name * 新遠端檔名稱(路徑-必須保證在同一路徑下) * * @param remote * 遠端檔案路徑 * * @return 是否成功 * * @throws IOException */ public FTPStatus rename(String name,String remote) throws IOException { ftpClient.enterLocalPassiveMode(); ftpClient.setFileType(FTP.BINARY_FILE_TYPE); FTPStatus result = null; FTPFile[] files = ftpClient.listFiles(remote); if (files.length == 1) { boolean status = ftpClient.rename(remote, name); result = status ? FTPStatus.Remote_Rename_Success : FTPStatus.Remote_Rename_Faild; } else { result = FTPStatus.Not_Exist_File; } Log.getLogger(this.getClass()).info("FTP伺服器檔名更新標識:"+result); return result; } /** * * 從FTP伺服器上下載檔案 * * @param fileName * 下載檔案的名字(包括字尾名) * * @param remote * 遠端檔案路徑 * * @param local * 本地檔案路徑 * * @return 是否成功 * * @throws IOException */ public FTPStatus download(String fileName,String remote,HttpServletResponse response) throws IOException { // 開啟輸出流彈出檔案儲存路徑選擇視窗 response.setContentType("application/octet-stream"); response.setContentType("application/OCTET-STREAM;charset=UTF-8"); response.setHeader("Content-Disposition", "attachment;filename=" +fileName); ftpClient.enterLocalPassiveMode(); ftpClient.setFileType(FTP.BINARY_FILE_TYPE); FTPStatus result; OutputStream out = response.getOutputStream(); boolean status = ftpClient.retrieveFile(remote, out); result=status?FTPStatus.Download_From_Break_Success:FTPStatus.Download_From_Break_Faild; Log.getLogger(this.getClass()).info("FTP伺服器檔案下載標識:"+result); out.close(); return result; } /** * * 從FTP伺服器上下載檔案 * * @param remote * 遠端檔案路徑 * * @param local * 本地檔案路徑 * * @return 是否成功 * * @throws IOException */ @SuppressWarnings("resource") public FTPStatus download(String remote, String local) throws IOException { ftpClient.enterLocalPassiveMode(); ftpClient.setFileType(FTP.BINARY_FILE_TYPE); FTPStatus result; File f = new File(local); FTPFile[] files = ftpClient.listFiles(remote); if (files.length != 1) { Log.getLogger(this.getClass()).info("遠端檔案不唯一"); return FTPStatus.File_Not_Unique; } long lRemoteSize = files[0].getSize(); if (f.exists()) { OutputStream out = new FileOutputStream(f, true); Log.getLogger(this.getClass()).info("本地檔案大小為:" + f.length()); if (f.length() >= lRemoteSize) { Log.getLogger(this.getClass()).info("本地檔案大小大於遠端檔案大小,下載中止"); return FTPStatus.Remote_smaller_local; } ftpClient.setRestartOffset(f.length()); boolean status = ftpClient.retrieveFile(remote, out); result=status?FTPStatus.Download_From_Break_Success:FTPStatus.Download_From_Break_Faild; out.close(); } else { OutputStream out = new FileOutputStream(f); boolean status = ftpClient.retrieveFile(remote, out); result=status?FTPStatus.Download_From_Break_Success:FTPStatus.Download_From_Break_Faild; out.close(); } return result; } /** * * 上傳檔案到FTP伺服器,支援斷點續傳 * * @param local * 本地檔名稱,絕對路徑 * * @param remote * 遠端檔案路徑,使用/home/directory1/subdirectory/file.ext * 按照Linux上的路徑指定方式,支援多級目錄巢狀,支援遞迴建立不存在的目錄結構 * * @return 上傳結果 * * @throws IOException */ @SuppressWarnings("resource") public FTPStatus upload(String local, String remote) throws IOException { // 設定PassiveMode傳輸 ftpClient.enterLocalPassiveMode(); // 設定以二進位制流的方式傳輸 ftpClient.setFileType(FTP.BINARY_FILE_TYPE); FTPStatus result; // 對遠端目錄的處理 String remoteFileName = remote; if (remote.contains("/")) { remoteFileName = remote.substring(remote.lastIndexOf("/") + 1); String directory = remote.substring(0, remote.lastIndexOf("/") + 1); if (!directory.equalsIgnoreCase("/") && !ftpClient.changeWorkingDirectory(directory)) { // 如果遠端目錄不存在,則遞迴建立遠端伺服器目錄 int start = 0; int end = 0; if (directory.startsWith("/")) { start = 1; } else { start = 0; } end = directory.indexOf("/", start); while (true) { String subDirectory = remote.substring(start, end); if (!ftpClient.changeWorkingDirectory(subDirectory)) { if (ftpClient.makeDirectory(subDirectory)) { ftpClient.changeWorkingDirectory(subDirectory); } else { Log.getLogger(this.getClass()).info("建立目錄失敗"); return FTPStatus.Create_Directory_Fail; } } start = end + 1; end = directory.indexOf("/", start); // 檢查所有目錄是否建立完畢 if (end <= start) { break; } } } } // 檢查遠端是否存在檔案 FTPFile[] files = ftpClient.listFiles(remoteFileName); if (files.length == 1) { long remoteSize = files[0].getSize(); File f = new File(local); long localSize = f.length(); if (remoteSize == localSize) { return FTPStatus.File_Exits; } else if (remoteSize > localSize) { return FTPStatus.Remote_Bigger_Local; } // 嘗試移動檔案內讀取指標,實現斷點續傳 InputStream is = new FileInputStream(f); if (is.skip(remoteSize) == remoteSize) { ftpClient.setRestartOffset(remoteSize); if (ftpClient.storeFile(remote, is)) { return FTPStatus.Upload_From_Break_Success; } } // 如果斷點續傳沒有成功,則刪除伺服器上檔案,重新上傳 if (!ftpClient.deleteFile(remoteFileName)) { return FTPStatus.Delete_Remote_Faild; } is = new FileInputStream(f); if (ftpClient.storeFile(remote, is)) { result = FTPStatus.Upload_New_File_Success; } else { result = FTPStatus.Upload_New_File_Failed; } is.close(); } else { InputStream is = new FileInputStream(local); if (ftpClient.storeFile(remoteFileName, is)) { result = FTPStatus.Upload_New_File_Success; } else { result = FTPStatus.Upload_New_File_Failed; } is.close(); } return result; } /** * * 斷開與遠端伺服器的連線 * * @throws IOException */ public void disconnect() throws IOException { if (ftpClient.isConnected()) { ftpClient.disconnect(); } } @Override public void run() { boolean status=false; // 建立FTP連線 try { ftpClient.connect(userInfo.getIp(), userInfo.getPort()); if (FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) { if (ftpClient.login(userInfo.getUsername(), userInfo.getPassword())) { status=true; } }else{ try { disconnect(); } catch (IOException e) { e.printStackTrace(); } } } catch (SocketException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } // FTP連線成功後執行相應的操作 if(status){ FTPStatus result=null; if(this.ftpType==FTPType.UPLOAD) { try { result=this.upload(userInfo.getLocal(), userInfo.getRemote());// 上傳檔案 } catch (IOException e) { Log.getLogger(ThreadFTPUtils.class).info("FTP上傳檔案異常:" + e.getMessage()); } }else if(this.ftpType==FTPType.DOWNLOAD) { try { result=this.download(userInfo.getRemote(), userInfo.getLocal());// 下載檔案 } catch (IOException e) { Log.getLogger(ThreadFTPUtils.class).info("FTP下載檔案異常:" + e.getMessage()); } }else if(this.ftpType==FTPType.RENAME) { try { result=this.rename(userInfo.getLocal(), userInfo.getRemote());// 修改名稱 } catch (IOException e) { Log.getLogger(ThreadFTPUtils.class).info("FTP修改檔名稱異常:" + e.getMessage()); } }else if(this.ftpType==FTPType.DELETE) { try { result=this.delete(userInfo.getRemote()); // 刪除檔案 } catch (IOException e) { Log.getLogger(ThreadFTPUtils.class).info("FTP刪除檔案異常:" + e.getMessage()); } } try { disconnect(); } catch (IOException e) { Log.getLogger(ThreadFTPUtils.class).info("FTP連線釋放異常:" + e.getMessage()); } Log.getLogger(this.getClass()).info("FTP操作狀態碼:"+result); } } public static void main(String[] args) { ThreadFTPUtils myFtp = new ThreadFTPUtils("192.168.1.200", 21, "duser", "HTPDuserXP32","C:\\Users\\Administrator\\Desktop\\swing.drawer.jar","/jars/boonya.jar",FTPType.UPLOAD); Thread thread=new Thread(myFtp); thread.start(); } } class UserInfo{ private String ip; //FTP伺服器的IP地址 private int port; //FTP伺服器埠 private String username;//登入使用者名稱 private String password;//登入密碼 private String local; //本地檔案或檔名 private String remote; //遠端檔案或路徑 public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } 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 getLocal() { return local; } public void setLocal(String local) { this.local = local; } public String getRemote() { return remote; } public void setRemote(String remote) { this.remote = remote; } public UserInfo() { } public UserInfo(String ip, int port, String username, String password, String local, String remote) { this.ip = ip; this.port = port; this.username = username; this.password = password; this.local = local; this.remote = remote; } }