• 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连接和登录



文件上传



文件下载


测试程序


完整程序

  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. }

参考链接



【FTP】FTP文件上传下载-支持断点续传的更多相关文章

  1. FTP文件上传并支持断点续传(一)—— win10 本地环境 ftp站点构建

    由于之前项目开发是采用是采用的FTP文件上传,就一直想学习,但由于FTP服务器是公司的,为了方便就像把本地变成ftp站点,其实很简单,但也有很多坑 这里简单介绍一下自己遇到的坑 一:开通本地的ftp权 ...

  2. PHP 大文件上传,支持断点续传,求具体方案、源码或者文件上传插件

    文件夹数据库处理逻辑 publicclass DbFolder { JSONObject root; public DbFolder() { this.root = new JSONObject(); ...

  3. ftp实现文件上传(下载)

    例子代码 package getUrlPic; import java.io.ByteArrayInputStream; import java.io.IOException; import java ...

  4. 转:【专题十一】实现一个基于FTP协议的程序——文件上传下载器

    引言: 在这个专题将为大家揭开下FTP这个协议的面纱,其实学习知识和生活中的例子都是很相通的,就拿这个专题来说,要了解FTP协议然后根据FTP协议实现一个文件下载器,就和和追MM是差不多的过程的,相信 ...

  5. Java实现FTP批量大文件上传下载篇1

    本文介绍了在Java中,如何使用Java现有的可用的库来编写FTP客户端代码,并开发成Applet控件,做成基于Web的批量.大文件的上传下载控件.文章在比较了一系列FTP客户库的基础上,就其中一个比 ...

  6. Python 基于Python实现Ftp文件上传,下载

    基于Python实现Ftp文件上传,下载   by:授客 QQ:1033553122 测试环境: Ftp客户端:Windows平台 Ftp服务器:Linux平台 Python版本:Python 2.7 ...

  7. 专题十一:实现一个基于FTP协议的程序——文件上传下载器

    引言: 在这个专题将为大家揭开下FTP这个协议的面纱,其实学习知识和生活中的例子都是很相通的,就拿这个专题来说,要了解FTP协议然后根据FTP协议实现一个文件下载器,就和和追MM是差不多的过程的,相信 ...

  8. 【ABAP系列】SAP ABAP 实现FTP的文件上传与下载

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP ABAP 实现FTP的文 ...

  9. HTTP文件上传服务器-支持超大文件HTTP断点续传的实现办法

    最近由于笔者所在的研发集团产品需要,需要支持高性能的大文件http上传,并且要求支持http断点续传.笔者在以前的博客如何实现支持大文件的高性能HTTP文件上传服务器已经介绍了实现大文件上传的一些基本 ...

随机推荐

  1. .net 下判断中英文字符串长度

    System.Text.Encoding.Default.GetBytes(str).Length

  2. Spring的依赖注入怎么理解

    先看一段代码假设你编写了两个类,一个是人(Person),一个是手机(Mobile). 人有时候需要用手机打电话,需要用到手机的dialUp方法. 传统的写法是这样: Java code public ...

  3. Python基于websocket实时通信的实现—GoEasy

    Python websocket实时消息推送 在这里我记录一下之前如何实现服务器端与客户端实时通信: 实现步骤如下: 1.        获取GoEasy appkey. 在goeasy官网上注册一个 ...

  4. git 较基础命令

    还需要进一步了解git的组织形式: git clone *.git 下载下来以git方式管理 如果直接下载压缩包做不到 git branch 分支相关命令 git checkout 可以换分支 git ...

  5. 09——绝不在构造和析构函数中调用virtual函数

    在base class构造期间,virtual函数不是virtual函数. 构造函数.析构函数中不要调用virtual函数.

  6. redis DB操作

    数据库操作 1)  REDIS是全部由KEY和VALUE值构成,对数据库的增删改查操作都是基于在通过key 映射到哈希槽 然后通过哈希槽进行单向链式遍历 查找到value和具体的key. 同样 在查看 ...

  7. Redis的简介与安装(windows)

    1.简介 Redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串). list(链表).set(集合).zset(sorte ...

  8. IC卡复位应答ATR的数据元和它们的意义

    ISO/IEC 7816-3标准中对ATR的数据串和数据元做了规定和描述.ATR的数据元和它们的意义: 数据元 说明 TS 起始字符 T0 格式字符 TA1,TB1,TC1,TD1,... 接口字符 ...

  9. css 居中

    今天来总结一下自己知道的居中方法: 一.水平居中 1.text-align:center;  文字水平居中,也可以放在父元素中,强行让子元素居中. 2.margin: 0 auto;   使子元素在父 ...

  10. Java控制台中输入中文输出乱码的解决办法

    Run---Run Configurations---Common---Encoding---Other---GBK Run Configurations里的Common中将编码方式改成GBK就正常了