【FTP】FTP文件上传下载-支持断点续传

时间:2022-02-02 11:41:03
  • 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连接和登录

【FTP】FTP文件上传下载-支持断点续传

【FTP】FTP文件上传下载-支持断点续传


文件上传

【FTP】FTP文件上传下载-支持断点续传

【FTP】FTP文件上传下载-支持断点续传

【FTP】FTP文件上传下载-支持断点续传

文件下载

【FTP】FTP文件上传下载-支持断点续传
【FTP】FTP文件上传下载-支持断点续传

测试程序

【FTP】FTP文件上传下载-支持断点续传

完整程序

  1. package com.sssppp.Communication;
  2. import java.io.File;
  3. import java.io.FileOutputStream;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.io.OutputStream;
  7. import java.io.PrintWriter;
  8. import java.io.RandomAccessFile;
  9. import org.apache.commons.net.PrintCommandListener;
  10. import org.apache.commons.net.ftp.FTP;
  11. import org.apache.commons.net.ftp.FTPClient;
  12. import org.apache.commons.net.ftp.FTPClientConfig;
  13. import org.apache.commons.net.ftp.FTPFile;
  14. import org.apache.commons.net.ftp.FTPReply;
  15. import org.apache.commons.net.io.CopyStreamEvent;
  16. import org.apache.commons.net.io.CopyStreamListener;
  17. /**
  18. * FTP进行文件上传和下载;
  19. * 支持断点续传;
  20. */
  21. public final class FTPUtil {
  22. private final FTPClient ftp = new FTPClient();
  23. /**
  24. *
  25. * @param hostname
  26. * 如:IP
  27. * @param port
  28. * @param username
  29. * @param password
  30. * @return
  31. * @throws IOException
  32. */
  33. public boolean connect(String hostname, int port, String username,
  34. String password) throws IOException {
  35. boolean debug = false;
  36. if (debug) {
  37. // 设置将过程中使用到的命令输出到控制台
  38. this.ftp.addProtocolCommandListener(new PrintCommandListener(
  39. new PrintWriter(System.out), true));
  40. }
  41. //设置系统类型
  42. final FTPClientConfig config = new FTPClientConfig(
  43. FTPClientConfig.SYST_UNIX);
  44. this.ftp.configure(config);
  45. try {
  46. this.ftp.connect(hostname, port);
  47. if (!FTPReply.isPositiveCompletion(this.ftp.getReplyCode())) {
  48. this.ftp.disconnect();
  49. System.err.println("FTP server refused connection.");
  50. return false;
  51. }
  52. } catch (IOException e) {
  53. if (this.ftp.isConnected()) {
  54. try {
  55. this.ftp.disconnect();
  56. } catch (IOException f) {
  57. }
  58. }
  59. System.err.println("Could not connect to server.");
  60. e.printStackTrace();
  61. return false;
  62. }
  63. if (!this.ftp.login(username, password)) {
  64. this.ftp.logout();
  65. System.err.println("Could not login to server.");
  66. return false;
  67. }
  68. return true;
  69. }
  70. public void disconnect() throws IOException {
  71. if (this.ftp.isConnected()) {
  72. try {
  73. this.ftp.logout();
  74. this.ftp.disconnect();
  75. } catch (IOException f) {
  76. }
  77. }
  78. }
  79. /**
  80. *
  81. * @param absSrcFileName
  82. * @param destDir
  83. * @param destFileName
  84. * @throws IOException
  85. */
  86. public void upLoadByFtp(String absSrcFileName, String destDir,
  87. String destFileName) throws IOException {
  88. // 创建并转到工作目录
  89. String absDstDir = this.ftp.printWorkingDirectory() + "/" + destDir;
  90. absDstDir = absDstDir.replaceAll("//", "/");
  91. createDirectory(absDstDir, this.ftp);
  92. // 设置各种属性
  93. this.ftp.setFileType(FTP.BINARY_FILE_TYPE);
  94. // Use passive mode as default because most of us are behind firewalls these days.
  95. this.ftp.enterLocalPassiveMode();
  96. this.ftp.setControlEncoding("utf-8");
  97. this.ftp.setBufferSize(1024);
  98. // 进度监听
  99. File srcFile = new File(absSrcFileName);
  100. this.ftp.setCopyStreamListener(new MyCopyStreamListener(srcFile.length()));
  101. FTPFile[] files = this.ftp.listFiles(destFileName);
  102. if (files.length == 1) {// 断点续传
  103. long dstFileSize = files[0].getSize();
  104. if (srcFile.length() <= dstFileSize) {// 文件已存在
  105. return;
  106. }
  107. boolean b = uploadFile(destFileName, srcFile, this.ftp, dstFileSize);
  108. if (!b) {// 如果断点续传没有成功,则删除服务器上文件,重新上传
  109. if (this.ftp.deleteFile(destFileName)) {
  110. uploadFile(destFileName, srcFile, this.ftp, 0);
  111. }else {
  112. System.err.println("Delete file fail.");
  113. }
  114. }
  115. } else {
  116. uploadFile(destFileName, srcFile, this.ftp, 0);
  117. }
  118. }
  119. /**
  120. *
  121. * @param remoteFileName
  122. * @param localFileName
  123. * @throws IOException
  124. */
  125. public void downLoadByFtp(String remoteFileName, String localFileName)
  126. throws IOException {
  127. InputStream input = null;
  128. FileOutputStream fos = null;
  129. // 设置各种属性
  130. this.ftp.setBufferSize(1024);
  131. this.ftp.setDataTimeout(1000 * 10);
  132. this.ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
  133. this.ftp.enterLocalPassiveMode();
  134. // 判断远程文件是否存在
  135. FTPFile[] files = this.ftp.listFiles(remoteFileName);
  136. if (files.length != 1) {
  137. System.err.println("Remote file not exist.");
  138. return;
  139. }
  140. //进度监听
  141. long remoteSize = files[0].getSize();
  142. this.ftp.setCopyStreamListener(new MyCopyStreamListener(remoteSize));
  143. File file = new File(localFileName);
  144. if (file.exists()) {
  145. long localSize = file.length();
  146. if (localSize >= remoteSize) {
  147. return;
  148. }
  149. System.out.println("@@@Break point download.@@@");
  150. fos = new FileOutputStream(file, true);// append模式
  151. this.ftp.setRestartOffset(localSize);
  152. } else {
  153. fos = new FileOutputStream(file); // override模式
  154. }
  155. input = this.ftp.retrieveFileStream(remoteFileName);
  156. byte[] b = new byte[8192];
  157. int n = 0;
  158. while (-1 != (n = input.read(b))) {
  159. if (Thread.currentThread().isInterrupted()) {
  160. break;
  161. }
  162. fos.write(b, 0, n);
  163. }
  164. if (input != null) {
  165. input.close();
  166. }
  167. if (fos != null) {
  168. fos.flush();
  169. fos.close();
  170. }
  171. if (!this.ftp.completePendingCommand()) {
  172. System.err.println("Download file fail.");
  173. this.ftp.logout();
  174. this.ftp.disconnect();
  175. }
  176. }
  177. /**
  178. *
  179. * @param destFileName
  180. * @param srcFile
  181. * @param ftpClient
  182. * @param dstFileSize 文件写入的起始位置; >0:表示断点续传,<=0:表示上传新文件
  183. * @return
  184. * @throws IOException
  185. */
  186. private boolean uploadFile(String destFileName, File srcFile,
  187. FTPClient ftpClient, long dstFileSize) throws IOException {
  188. RandomAccessFile input = null;
  189. OutputStream fout = null;
  190. input = new RandomAccessFile(srcFile, "r"); // 只读模式
  191. if (dstFileSize > 0) {// 断点续传
  192. fout = ftpClient.appendFileStream(destFileName);
  193. input.seek(dstFileSize);
  194. ftpClient.setRestartOffset(dstFileSize);
  195. } else {
  196. fout = ftpClient.storeFileStream(destFileName);
  197. }
  198. byte[] b = new byte[8192]; // 缓存大小
  199. int n = 0;
  200. while (-1 != (n = input.read(b))) {
  201. if (Thread.currentThread().isInterrupted()) {
  202. break;
  203. }
  204. fout.write(b, 0, n);
  205. }
  206. if (input != null) {
  207. input.close();
  208. }
  209. if (fout != null) {
  210. fout.flush();
  211. fout.close();
  212. }
  213. if (!ftpClient.completePendingCommand()) {
  214. System.err.println("Upload file fail.");
  215. ftpClient.logout();
  216. ftpClient.disconnect();
  217. return false;
  218. }
  219. return true;
  220. }
  221. /**
  222. * 在FTP服务器上创建并转到工作目录
  223. *
  224. * @param relativePath
  225. * 相对工作路径,不包含文件名:如 dd/11/22/33
  226. * @param ftpClient
  227. * 录创建是否成功
  228. * @return
  229. * @throws IOException
  230. */
  231. private boolean createDirectory(String relativePath, FTPClient ftpClient)
  232. throws IOException {
  233. if (!relativePath.startsWith("/")) {
  234. relativePath = "/" + relativePath;
  235. }
  236. String dir = (ftpClient.printWorkingDirectory().equals("/") ? ""
  237. : ftpClient.printWorkingDirectory()) + relativePath;
  238. if (!ftpClient.changeWorkingDirectory(dir)) {
  239. //目录不存在,则创建各级目录
  240. for (String subDir : relativePath.split("/")) {
  241. if (!subDir.equals("")) {
  242. String newDir = ftpClient.printWorkingDirectory() + "/"
  243. + subDir;
  244. ftpClient.mkd(newDir);
  245. if (!ftpClient.changeWorkingDirectory(newDir)) {
  246. return false;
  247. }
  248. }
  249. }
  250. }
  251. return true;
  252. }
  253. /**
  254. * 进度监听器
  255. */
  256. private class MyCopyStreamListener implements CopyStreamListener {
  257. private long totalSize = 0;
  258. private long percent = -1; // 进度
  259. /**
  260. * 文件的总大小
  261. * @param totalSize
  262. */
  263. public MyCopyStreamListener(long totalSize) {
  264. super();
  265. this.totalSize = totalSize;
  266. }
  267. @Override
  268. public void bytesTransferred(CopyStreamEvent event) {
  269. bytesTransferred(event.getTotalBytesTransferred(),
  270. event.getBytesTransferred(), event.getStreamSize());
  271. }
  272. //totalBytesTransferred:当前总共已传输字节数;
  273. //bytesTransferred:最近一次传输字节数
  274. @Override
  275. public void bytesTransferred(long totalBytesTransferred,
  276. int bytesTransferred, long streamSize) {
  277. if (percent >= totalBytesTransferred * 100 / totalSize) {
  278. return;
  279. }
  280. percent = totalBytesTransferred * 100 / totalSize;
  281. System.out.println("Completed " + totalBytesTransferred + "("
  282. + percent + "%) out of " + totalSize + ".");
  283. }
  284. }
  285. public static void main(String[] args) throws IOException {
  286. String hostname = "10.180.137.241";
  287. String username = "xxx";
  288. String password = "xxx";
  289. int port = 21;
  290. FTPUtil ftp = new FTPUtil();
  291. //上传文件
  292. String absSrcFileName = "C:\\tmp\\m2eclipse1.zip";
  293. String destDir = "ww/11/22/33";
  294. String destFileName = "m2eclipse1.zip";
  295. ftp.connect(hostname, port, username, password);
  296. ftp.upLoadByFtp(absSrcFileName, destDir, destFileName);
  297. ftp.disconnect();
  298. // 下载文件
  299. String localFileName = "C:\\tmp\\m2eclipse-download3333.zip";
  300. String remoteFileName = "/ww/11/22/33/m2eclipse.zip";
  301. ftp.connect(hostname, port, username, password);
  302. ftp.downLoadByFtp(remoteFileName, localFileName);
  303. ftp.disconnect();
  304. }
  305. }

参考链接