最近在给我的开源下载框架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原生实现多线程断点续传功能
# 断点续传
# 器上
# 客户端
# 连接到
# 还需要
# 的是
# 几个
# 多线程
# 在此
# 有所不同
# 很简单
# 便可
# 上传
# 则会
# 进行下载
# 如果你
# 是在
# 你可以
# 就能
# 也就
相关文章:
建站上市公司网站建设方案与SEO优化服务定制指南
佛山网站制作系统,佛山企业变更地址网上办理步骤?
建站之星后台管理:高效配置与模板优化提升用户体验
魔方云NAT建站如何实现端口转发?
建站之星代理费用多少?最新价格详情介绍
临沂网站制作企业,临沂第三中学官方网站?
如何快速启动建站代理加盟业务?
宁波自助建站系统如何快速打造专业企业网站?
网站建设设计制作营销公司南阳,如何策划设计和建设网站?
,网页ppt怎么弄成自己的ppt?
黑客入侵网站服务器的常见手法有哪些?
企业网站制作公司网页,推荐几家专业的天津网站制作公司?
*服务器网站为何频现安全漏洞?
济南网站建设制作公司,室内设计网站一般都有哪些功能?
Python路径拼接规范_跨平台处理说明【指导】
建设网站制作价格,怎样建立自己的公司网站?
如何在香港免费服务器上快速搭建网站?
公司网站制作需要多少钱,找人做公司网站需要多少钱?
深圳网站制作费用多少钱,读秀,深圳文献港这样的网站很多只提供网上试读,但有些人只要提供试读的文章就能全篇下载,这个是怎么弄的?
如何在宝塔面板中创建新站点?
php条件判断怎么写_ifelse和switchcase的使用区别【对比】
建站之星展会模版如何一键下载生成?
大同网页,大同瑞慈医院官网?
免费网站制作appp,免费制作app哪个平台好?
设计网站制作公司有哪些,制作网页教程?
b2c电商网站制作流程,b2c水平综合的电商平台?
如何高效配置香港服务器实现快速建站?
如何用PHP快速搭建CMS系统?
安徽网站建设与外贸建站服务专业定制方案
济南专业网站制作公司,济南信息工程学校怎么样?
重庆网站制作公司哪家好,重庆中考招生办官方网站?
如何在Golang中使用encoding/gob序列化对象_存储和传输数据
实现点击下箭头变上箭头来回切换的两种方法【推荐】
建站与域名管理如何高效结合?
弹幕视频网站制作教程下载,弹幕视频网站是什么意思?
建站之星各版本价格是多少?
如何快速登录WAP自助建站平台?
建站之星后台管理系统如何操作?
标准网站视频模板制作软件,现在有哪个网站的视频编辑素材最齐全的,背景音乐、音效等?
建站之星如何通过成品分离优化网站效率?
如何通过VPS建站实现广告与增值服务盈利?
如何在建站之星绑定自定义域名?
专业型网站制作公司有哪些,我设计专业的,谁给推荐几个设计师兼职类的网站?
成都网站制作报价公司,成都工业用气开户费用?
网站制作话术技巧,网站推广做的好怎么话术?
车管所网站制作流程,交警当场开简易程序处罚决定书,在交警网站查询不到怎么办?
潍坊网站制作公司有哪些,潍坊哪家招聘网站好?
c++怎么用jemalloc c++替换默认内存分配器【性能】
北京网站制作网页,网站升级改版需要多久?
天津个人网站制作公司,天津网约车驾驶员从业资格证官网?
*请认真填写需求信息,我们会在24小时内与您取得联系。