FTP多线程下载

 Android 中除了HTTP协议下载之外还可以使用FTP下载,HTTP的断点下载使用RandomAccessFile文件,FTP使用reset命令。和HTTP所不同的是,FTP并没有提供文件区间的API,因此,FTP在分段下载中,只有起始位置而没有结束位置。

因此,你需要在指定位置手动停止线程。
FTP多线程断点下载实现主要有以下步骤:
1,登录服务器
FTP协议和HTTP协议有所不同,使用FTP进行下载时,你需要进行登录操作。
当然,如果你服务器没有登录功能,你可以忽略登录操作。

FTPClient client = new FTPClient();
client.connect(serverIp, port); //连接到FTP服务器
client.login(userName, passsword);

在登录成功之后,还要验证登陆是否成功

int reply = client.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
    client.disconnect();
    Log.d(TAG, "无法连接到ftp服务器,错误码为:" + reply);
    return;
 }

由于FTP协议中,连接成功的状态有多个,因此需要通过FTPReply.isPositiveCompletion(reply)用于验证是否成功连接到FTP服务器。
2,获取文件信息
在连接到FTP服务器后,就需要开始获取下载最重要的几个参数(文件长度、文件名)。客户端可以通过client.listFiles(remotePath)获取FTP服务器上该路径的文件列表。如果路径是文件,只会返回一个长度为1的数组,如果该路径为文件夹,则会返回该文件夹下对应的所有文件。

String remotePath = "/upload/qjnn.apk"; //FTP服务器上文件路径
FTPFile[] files = client.listFiles(remotePath);
FTPFile file = files[0];  //文件信息
long size = file.getSize();
String fileaName = file.getName();

注意文件路径中不能有中文名。
由于FTP服务器默认的编码是ISO-8859-1,因此,客户端在获取文件信息时需要进行以下操作:
1)需要请求服务器使用UTF-8编码(如果服务器支持的话),如果服务器不支持开启UTF-8编码,那么客户端需要指定字符串编码格式。
2)客户端在请求remotePath路径、获取文件名时,都需要对路径进行编码转换处理。
以下是处理代码:

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));

3,配置每条线程的下载区间

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);

上面代码完成两个操作:
1)在文件下载前,先从本地文件中读取当前下载的每一条线程的下载情况。
2)如果下载记录存在,从记录位置开始下载,如果记录不存在,则重新开始下载。
4,每个线程的下载任务
由于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);
    }
 }
 这里还有几个坑需要处理一下:
 1)对于FTP客户端来说,一般需要设置被动模式。
 2)在获取文件流后,还需要使用FTPReply.isPositivePreliminary(reply)进行第二次命令判断。
至此FTP多线程断点下载已经完成,最后说一下多线程断点上传的思路:

FTP 文件断点续传的方式原理和下载的都差不多:
1)都是在停止的时候记录停止位置,重新开始下载的时候从指定位置通过REST命令恢复断点。
2)都需要在任务执行前获取文件信息,比对服务器上的文件。
而和下载有区别的是:
1)FTP上传时需要指定工作目录、在远程服务器上创建文件夹。
2)需要服务器给用户打开删除和读入IO的权限,否则会出现550权限错误问题。
3)上传文件需要storeFileStream获取outputStream流。

参考文章:http://www.jianshu.com/p/b5231857f0a3

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,585评论 18 139
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,169评论 11 349
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,852评论 6 13
  • .
    铭兮阅读 69评论 0 0
  • 死生楔阔的誓言太复杂太复杂,复杂到必须要用一生去仔细的勾勒
    九木先生阅读 176评论 0 0