Android FTP 多线程断点续传下载\上传的实例

最近在给我的开源下载框架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服务器上该路径的文件列表。

  • 如果路径是文件,只会返回一个长度为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上的服务器上的文件名有中文或路径有中文,那么上述代码,你将获取不到正确的文件信息。

正确的写法

由于FTP服务器默认的编码是ISO-8859-1,因此,客户端在获取文件信息时

  • 需要请求服务器使用UTF-8编码(如果服务器支持的话),如果服务器不支持开启UTF-8编码,那么客户端需要指定字符串编码格式
  • 客户端在请求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));

通过以上代码,便可以获取到正确的文件信息。

文件下载

配置每条线程的下载区间

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

这里还有几个坑需要处理一下:

  1. 对于FTP客户端来说,一般需要设置被动模式,被动模式和主动模式的区别
  2. 在获取文件流后,还需要使用FTPReply.isPositivePreliminary(reply)进行第二次命令判断

关于FTP文件上传

FTP 文件断点续传的方式原理和下载的都差不多:

  1. 都是在停止的时候记录停止位置,重新开始下载的时候从指定位置通过REST命令恢复断点。
  2. 都需要在任务执行前获取文件信息,比对服务器上的文件。

而和下载有区别的是:

  1. FTP上传时需要指定工作目录、在远程服务器上创建文件夹
  2. 需要服务器给用户打开删除和读入IO的权限,否则会出现550权限错误问题
  3. 上传文件需要storeFileStream获取outputStream流

最终效果

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android中FTP上传、下载的功能实现(含进度)

    Android中使用的FTP上传.下载,含有进度. 代码部分主要分为三个文件:MainActivity,FTP,ProgressInputStream 1. MainActivity package com.ftp; import java.io.File; import java.io.IOException; import java.util.LinkedList; import com.ftp.FTP.DeleteFileProgressListener; import com.ftp.F

  • Android关于FTP文件上传和下载功能详解

    本文实例为大家分享了Android九宫格图片展示的具体代码,供大家参考,具体内容如下 此篇博客为整理文章,供大家学习. 1.首先下载commons-net  jar包,可以百度下载. FTP的文件上传和下载的工具类: package ryancheng.example.progressbar; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.Outpu

  • Android FTP 多线程断点续传下载\上传的实例

    最近在给我的开源下载框架Aria增加FTP断点续传下载和上传功能,在此过程中,爬了FTP的不少坑,终于将功能实现了,在此把一些核心功能点记录下载. FTP下载原理 FTP单线程断点续传 FTP和传统的HTTP协议有所不同,由于FTP没有所谓的头文件,因此我们不能像HTTP那样通过设置header向服务器指定下载区间. 但是FTP协议提供了一个更好用的命令REST用于从指定位置恢复任务,同时FTP协议也提供了一个命令SIZE用于获取下载的文件大小,有了这两个命令,FTP断点续传也就没有什么问题.

  • Android使用ftp方式实现文件上传和下载

    近期在工作上一直再维护平台OTA在线升级项目,其中关于这个升级文件主要是存放于ftp服务器上的,然后客户端通过走ftp协议方式下载至本地Android机进行一个系统升级操作.那么今天将对ftp实现文件上传和下载进行一个使用总结,关于ftp这方面的理论知识如果不是太了解的各位道友,那么请移步HTTP和FTP的区别的一些理论知识 作个具体的了解或者查阅相关资料.那么先看看个人工作项目这个OTA升级效果图吧.如下: 下面是具体的接口实现: 那么相关ftp的操作,已经被封装到ota.ftp这个包下,各位

  • Android使用ftp方式实现文件上传和下载功能

    近期在工作上一直再维护平台OTA在线升级项目,其中关于这个升级文件主要是存放于ftp服务器上的,然后客户端通过走ftp协议方式下载至本地Android机进行一个系统升级操作.那么今天将对ftp实现文件上传和下载进行一个使用总结,关于ftp这方面的理论知识如果不是太了解的各位道友,那么请移步HTTP和FTP的区别的一些理论知识 作个具体的了解或者查阅相关资料.那么先看看个人工作项目这个OTA升级效果图吧.如下: 下面是具体的接口实现: 那么相关ftp的操作,已经被封装到ota.ftp这个包下,各位

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

    本文介绍了在Java中,如何使用Java现有的可用的库来编写FTP客户端代码,并开发成Applet控件,做成基于Web的批量.大文件的上传下载控件.文章在比较了一系列FTP客户库的基础上,就其中一个比较通用且功能较强的j-ftp类库,对一些比较常见的功能如进度条.断点续传.内外网的映射.在Applet中回调JavaScript函数等问题进行详细的阐述及代码实现,希望通过此文起到一个抛砖引玉的作用. 一.引子 笔者在实施一个项目过程中出现了一种基于Web的文件上传下载需求.在全省(或全国)各地的用

  • Android编程开发实现多线程断点续传下载器实例

    本文实例讲述了Android编程开发实现多线程断点续传下载器.分享给大家供大家参考,具体如下: 使用多线程断点续传下载器在下载的时候多个线程并发可以占用服务器端更多资源,从而加快下载速度,在下载过程中记录每个线程已拷贝数据的数量,如果下载中断,比如无信号断线.电量不足等情况下,这就需要使用到断点续传功能,下次启动时从记录位置继续下载,可避免重复部分的下载.这里采用数据库来记录下载的进度. 效果图:   断点续传 1.断点续传需要在下载过程中记录每条线程的下载进度 2.每次下载开始之前先读取数据库

  • Android多线程断点续传下载功能实现代码

    原理 其实断点续传的原理很简单,从字面上理解,所谓断点续传就是从停止的地方重新下载. 断点:线程停止的位置. 续传:从停止的位置重新下载. 用代码解析就是: 断点:当前线程已经下载完成的数据长度. 续传:向服务器请求上次线程停止位置之后的数据. 原理知道了,功能实现起来也简单.每当线程停止时就把已下载的数据长度写入记录文件,当重新下载时,从记录文件读取已经下载了的长度.而这个长度就是所需要的断点. 续传的实现也简单,可以通过设置网络请求参数,请求服务器从指定的位置开始读取数据. 而要实现这两个功

  • Android Http实现文件的上传和下载

    最近做一个项目,其中涉及到文件的上传和下载功能,大家都知道,这个功能实现其实已经烂大街了,遂.直接从网上荡了一堆代码用,结果,发现网上的代码真是良莠不齐,不是写的不全面,就是有问题,于是自己重新整理了一番,把它们发出来,希望更多人能受用. 文件上传 通过org.apache.commons.httpclient.HttpClient来实现文件上传,该jar包可以直接从网上所搜.下载. /** * @param mContext 上下文 * @param targetUrl 文件上传地址 * @p

  • Android编程实现图片的上传和下载功能示例

    本文实例讲述了Android编程实现图片的上传和下载功能.分享给大家供大家参考,具体如下: 在实现一个Android的WEB服务客户端,比如微博,论坛客户端时,经常会使用到图片的上传和下载.在这里介绍如何利用HttpClient实现图片的上传和下载功能. 1 图片上传:上传图片时,首先获得图片的路径,创建文件,并将图片转化为字节流写入到request,并发送该请求. 客户端代码: File file = new File(imageUrl); String httpUrl = httpDomai

  • Android实现网络多线程断点续传下载实例

    我们编写的是Andorid的HTTP协议多线程断点下载应用程序.直接使用单线程下载HTTP文件对我们来说是一件非常简单的事.那么,多线程断点需要什么功能? 1.多线程下载, 2.支持断点. 使用多线程的好处:使用多线程下载会提升文件下载的速度.那么多线程下载文件的过程是: (1)首先获得下载文件的长度,然后设置本地文件的长度. HttpURLConnection.getContentLength();//获取下载文件的长度 RandomAccessFile file = new RandomAc

  • Android实现相册中图片上传或下载

    本文实例为大家分享了Android实现相册中图片上传或下载的具体代码,供大家参考,具体内容如下 目标效果: 打开相册选择一张图片,会显示到上方的ImageView中并存储到Bmob中,存储后进入Bmob后台,复制刚才添加的数据的objectId,粘贴到代码指定出,然后运行,点击下载会在下方的ImageView显示刚才上传的图片,这里的下载是指定objectId,可以进行动态获取objectId进行下载. 1.activity_main.xml页面设置布局. activity_main.xml页面

随机推荐