最近在给我的开源下载框架Aria增加FTP断点续传下载和上传功能,在此过程中,爬了FTP的不少坑,终于将功能实现了,在此把一些核心功能点记录下载。

FTP下载原理
FTP单线程断点续传
FTP和传统的HTTP协议有所不同,由于FTP没有所谓的头文件,因此我们不能像HTTP那样通过设置header向服务器指定下载区间。
但是FTP协议提供了一个更好用的命令REST用于从指定位置恢复任务,同时FTP协议也提供了一个命令SIZE用于获取下载的文件大小,有了这两个命令,FTP断点续传也就没有什么问题。
FTP断点续传的原理和HTTP的断点续传原理差不多,在暂停时记录文件的停止位置,再次下载时,先读取记录的位置,如果位置存在,则通过REST命令告诉服务器从指定区间进行下载。
FTP多线程断点续传
多线程下载的原理和HTTP多线程下载的原理差不多。先获取文件大小,然后根据线程数,对整个文件进行分段下载,在任务停止时,记录每一条线程的暂停位置,重新开始下载,每一条线程读取对应的下载记录,然后每一线程从指定位置开始下载。
分段下载
和HTTP所不同的是,FTP并没有提供文件区间的API,因此,FTP在分段下载中,只有起始位置而没有结束位置。
因此,你需要在指定位置手动停止线程。
功能实现
本文使用将采用apache commons-net实现FTP断点续传下载\上传功能。<br>
通过下文的几步操作,你就能很简单的实现FTP断点续传。
登录
FTP协议和HTTP协议有所不同,使用FTP进行下载时,你需要进行登录操作。
当然,如果你服务器没有登录功能,你可以忽略登录操作。
FTPClient client = new FTPClient(); client.connect(serverIp, port); //连接到FTP服务器 client.login(userName, passsword);
通过上面三行代码,就可以很简单的登录到FTP服务器上。
在进行登录后,还需要验证是否登录成功
int reply = client.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
client.disconnect();
Log.d(TAG, "无法连接到ftp服务器,错误码为:" + reply);
return;
}
由于FTP协议中,连接成功的状态有多个,因此需要通过FTPReply.isPositiveCompletion(reply)用于验证是否成功连接到FTP服务器。
文件信息获取
在连接到FTP服务器后,就需要开始获取下载最重要的几个参数(文件长度、文件名)。
客户端可以通过client.listFiles(remotePath)获取FTP服务器上该路径的文件列表。
String remotePath = "/upload/qjnn.apk"; //FTP服务器上文件路径 FTPFile[] files = client.listFiles(remotePath); FTPFile file = files[0]; //文件信息 long size = file.getSize(); String fileaName = file.getName();
如果你的文件为英文名,并且路径中没有中文,那么通过上述代码,便可以获取到正确的文件信息。
但如果FTP上的服务器上的文件名有中文或路径有中文,那么上述代码,你将获取不到正确的文件信息。
正确的写法
由于FTP服务器默认的编码是ISO-8859-1,因此,客户端在获取文件信息时
String remotePath = "/upload/qjnn.apk"; //FTP服务器上文件路径
String charSet = "UTF-8";
if (!FTPReply.isPositiveCompletion(client.sendCommand("OPTS UTF8", "ON"))) { //向服务器请求使用"UTF-8"编码
charSet = "GBK";
}
FTPFile[] files = client.listFiles(new String(remotePath.getBytes(charSet), "ISO-8859-1")); //对remotePath进行编码转换
FTPFile file = files[0]; //文件信息
long size = file.getSize();
String fileaName = new String(fileName.getBytes(), Charset.forName(charSet));
通过以上代码,便可以获取到正确的文件信息。
文件下载
配置每条线程的下载区间
long fileLength = mEntity.getFileSize();
Properties pro = CommonUtil.loadConfig(mConfigFile);
int blockSize = (int) (fileLength / mThreadNum);
int[] recordL = new int[mThreadNum];
for (int i = 0; i < mThreadNum; i++) {
recordL[i] = -1;
}
int rl = 0;
for (int i = 0; i < mThreadNum; i++) {
long startL = i * blockSize, endL = (i + 1) * blockSize;
Object state = pro.getProperty(mTempFile.getName() + "_state_" + i);
if (state != null && Integer.parseInt(state + "") == 1) { //该线程已经完成
if (resumeRecordLocation(i, startL, endL)) return;
continue;
}
//分配下载位置
Object record = pro.getProperty(fileName + "_record_" + i);
//如果有记录,则恢复下载
if (record != null && Long.parseLong(record + "") >= 0) {
Long r = Long.parseLong(record + "");
mConstance.CURRENT_LOCATION += r - startL;
Log.d(TAG, "任务【" + mEntity.getFileName() + "】线程__" + i + "__恢复下载");
startL = r;
recordL[rl] = i;
rl++;
} else {
recordL[rl] = i;
rl++;
}
//最后一个线程的结束位置即为文件的总长度
if (i == (mThreadNum - 1)) endL = fileLength;
//创建分段线程
AbsThreadTask task = createSingThreadTask(i, startL, endL, fileLength);
if (task == null) return;
mTask.put(i, task);
}
startSingleTask(recordL);
在上面的代码中,主要做了两步操作:
FTP 分段线程区间自动停止
由于FTP协议没有区间下载的原因,为了让线程只下载特定区间的内容,需要客户端在单条线程累计读的数据长度已经超过了所分配的区间长度的时候,停止该条线程。
client.enterLocalPassiveMode(); //设置被动模式
client.setFileType(FTP.BINARY_FILE_TYPE); //设置文件传输模式
client.setRestartOffset(mConfig.START_LOCATION); //设置恢复下载的位置
client.allocate(mBufSize);
is = client.retrieveFileStream(new String(remotePath.getBytes(charSet), SERVER_CHARSET));
//发送第二次指令时,还需要再做一次判断
reply = client.getReplyCode();
if (!FTPReply.isPositivePreliminary(reply)) {
client.disconnect();
fail(mChildCurrentLocation, "获取文件信息错误,错误码为:" + reply, null);
return;
}
file = new BufferedRandomAccessFile(mConfig.TEMP_FILE, "rwd", mBufSize);
file.seek(mConfig.START_LOCATION);
byte[] buffer = new byte[mBufSize];
int len;
while ((len = is.read(buffer)) != -1) {
//如果该条线程读取的数据长度大于所分配的区间长度,则只能读到区间的最大长度
if (mChildCurrentLocation + len >= mConfig.END_LOCATION) {
len = (int) (mConfig.END_LOCATION - mChildCurrentLocation);
file.write(buffer, 0, len);
progress(len);
break;
} else {
file.write(buffer, 0, len);
progress(len);
}
}
这里还有几个坑需要处理一下:
关于FTP文件上传
FTP 文件断点续传的方式原理和下载的都差不多:
而和下载有区别的是:
最终效果
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# Android
# 多线程断点续传下载
# 多线程断点续传上传
# android实现多线程下载文件(支持暂停、取消、断点续传)
# Android多线程+单线程+断点续传+进度条显示下载功能
# Android多线程断点续传下载功能实现代码
# Android多线程断点续传下载示例详解
# Android 使用AsyncTask实现多任务多线程断点续传下载
# Android实现网络多线程断点续传下载实例
# Android编程开发实现多线程断点续传下载器实例
# PC版与Android手机版带断点续传的多线程下载
# Android 使用AsyncTask实现多线程断点续传
# android原生实现多线程断点续传功能
# 断点续传
# 器上
# 客户端
# 连接到
# 还需要
# 的是
# 几个
# 多线程
# 在此
# 有所不同
# 很简单
# 便可
# 上传
# 则会
# 进行下载
# 如果你
# 是在
# 你可以
# 就能
# 也就
相关文章:
XML的“混合内容”是什么 怎么用DTD或XSD定义
建站主机如何安装配置?新手必看操作指南
建站主机如何选?性能与价格怎样平衡?
如何在新浪SAE免费搭建个人博客?
如何在Windows环境下新建FTP站点并设置权限?
5种Android数据存储方式汇总
如何确保西部建站助手FTP传输的安全性?
如何快速生成凡客建站的专业级图册?
相册网站制作软件,图片上的网址怎么复制?
如何在沈阳梯子盘古建站优化SEO排名与功能模块?
如何设置并定期更换建站之星安全管理员密码?
武汉网站设计制作公司,武汉有哪些比较大的同城网站或论坛,就是里面都是武汉人的?
如何选择高效便捷的WAP商城建站系统?
教育培训网站制作流程,请问edu教育网站的域名怎么申请?
如何解决ASP生成WAP建站中文乱码问题?
如何通过西部数码建站助手快速创建专业网站?
如何制作公司的网站链接,公司想做一个网站,一般需要花多少钱?
制作电商网页,电商供应链怎么做?
如何选择美橙互联多站合一建站方案?
行程制作网站有哪些,第三方机票电子行程单怎么开?
制作网站的模板软件,网站怎么建设?
招商网站制作流程,网站招商广告语?
怎么用手机制作网站链接,dw怎么把手机适应页面变成网页?
如何快速建站并高效导出源代码?
南阳网站制作公司推荐,小学电子版试卷去哪里找资源好?
在线ppt制作网站有哪些软件,如何把网页的内容做成ppt?
制作旅游网站html,怎样注册旅游网站?
视频网站app制作软件,有什么好的视频聊天网站或者软件?
建站主机选购指南与交易推荐:核心配置解析
如何在云主机快速搭建网站站点?
香港网站服务器数量如何影响SEO优化效果?
独立制作一个网站多少钱,建立网站需要花多少钱?
佛山网站制作系统,佛山企业变更地址网上办理步骤?
专业的网站制作设计是什么,如何制作一个企业网站,建设网站的基本步骤有哪些?
制作网站公司那家好,网络公司是做什么的?
南平网站制作公司,2025年南平市事业单位报名时间?
深圳网站制作设计招聘,关于服装设计的流行趋势,哪里的资料比较全面?
湖州网站制作公司有哪些,浙江中蓝新能源公司官网?
手机怎么制作网站教程步骤,手机怎么做自己的网页链接?
沈阳制作网站公司排名,沈阳装饰协会官方网站?
Swift中循环语句中的转移语句 break 和 continue
详解jQuery中基本的动画方法
C++中引用和指针有什么区别?(代码说明)
Android自定义listview布局实现上拉加载下拉刷新功能
如何在阿里云购买域名并搭建网站?
魔方云NAT建站如何实现端口转发?
高性能网站服务器配置指南:安全稳定与高效建站核心方案
制作网站的软件下载免费,今日头条开宝箱老是需要下载怎么回事?
如何在七牛云存储上搭建网站并设置自定义域名?
建站之星24小时客服电话如何获取?
*请认真填写需求信息,我们会在24小时内与您取得联系。