最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android FTP 多線程斷點續(xù)傳下載\上傳的實例

 更新時間:2017年08月03日 09:50:25   作者:AriaLyy  
本篇文章主要介紹了Android FTP 多線程斷點續(xù)傳下載\上傳的實例,具有一定的參考價值,有興趣的可以了解一下

最近在給我的開源下載框架Aria增加FTP斷點續(xù)傳下載和上傳功能,在此過程中,爬了FTP的不少坑,終于將功能實現(xiàn)了,在此把一些核心功能點記錄下載。

FTP下載原理

FTP單線程斷點續(xù)傳

FTP和傳統(tǒng)的HTTP協(xié)議有所不同,由于FTP沒有所謂的頭文件,因此我們不能像HTTP那樣通過設(shè)置header向服務(wù)器指定下載區(qū)間。

但是FTP協(xié)議提供了一個更好用的命令REST用于從指定位置恢復任務(wù),同時FTP協(xié)議也提供了一個命令SIZE用于獲取下載的文件大小,有了這兩個命令,F(xiàn)TP斷點續(xù)傳也就沒有什么問題。

FTP斷點續(xù)傳的原理和HTTP的斷點續(xù)傳原理差不多,在暫停時記錄文件的停止位置,再次下載時,先讀取記錄的位置,如果位置存在,則通過REST命令告訴服務(wù)器從指定區(qū)間進行下載。

FTP多線程斷點續(xù)傳

多線程下載的原理和HTTP多線程下載的原理差不多。先獲取文件大小,然后根據(jù)線程數(shù),對整個文件進行分段下載,在任務(wù)停止時,記錄每一條線程的暫停位置,重新開始下載,每一條線程讀取對應(yīng)的下載記錄,然后每一線程從指定位置開始下載。

分段下載

和HTTP所不同的是,F(xiàn)TP并沒有提供文件區(qū)間的API,因此,F(xiàn)TP在分段下載中,只有起始位置而沒有結(jié)束位置。
因此,你需要在指定位置手動停止線程。

功能實現(xiàn)

本文使用將采用apache commons-net實現(xiàn)FTP斷點續(xù)傳下載\上傳功能。<br>

通過下文的幾步操作,你就能很簡單的實現(xiàn)FTP斷點續(xù)傳。

登錄

FTP協(xié)議和HTTP協(xié)議有所不同,使用FTP進行下載時,你需要進行登錄操作。

當然,如果你服務(wù)器沒有登錄功能,你可以忽略登錄操作。

FTPClient client = new FTPClient();
client.connect(serverIp, port); //連接到FTP服務(wù)器
client.login(userName, passsword);

通過上面三行代碼,就可以很簡單的登錄到FTP服務(wù)器上。

在進行登錄后,還需要驗證是否登錄成功

int reply = client.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
  client.disconnect();
  Log.d(TAG, "無法連接到ftp服務(wù)器,錯誤碼為:" + reply);
  return;
 }

由于FTP協(xié)議中,連接成功的狀態(tài)有多個,因此需要通過FTPReply.isPositiveCompletion(reply)用于驗證是否成功連接到FTP服務(wù)器。

文件信息獲取

在連接到FTP服務(wù)器后,就需要開始獲取下載最重要的幾個參數(shù)(文件長度、文件名)。

客戶端可以通過client.listFiles(remotePath)獲取FTP服務(wù)器上該路徑的文件列表。

  • 如果路徑是文件,只會返回一個長度為1的數(shù)組。
  • 如果該路徑為文件夾,則會返回該文件夾下對應(yīng)的所有文件。
String remotePath = "/upload/qjnn.apk"; //FTP服務(wù)器上文件路徑
FTPFile[] files = client.listFiles(remotePath);
FTPFile file = files[0]; //文件信息
long size = file.getSize();
String fileaName = file.getName();

如果你的文件為英文名,并且路徑中沒有中文,那么通過上述代碼,便可以獲取到正確的文件信息。

但如果FTP上的服務(wù)器上的文件名有中文或路徑有中文,那么上述代碼,你將獲取不到正確的文件信息。

正確的寫法

由于FTP服務(wù)器默認的編碼是ISO-8859-1,因此,客戶端在獲取文件信息時

  • 需要請求服務(wù)器使用UTF-8編碼(如果服務(wù)器支持的話),如果服務(wù)器不支持開啟UTF-8編碼,那么客戶端需要指定字符串編碼格式
  • 客戶端在請求remotePath路徑、獲取文件名時,都需要對路徑進行編碼轉(zhuǎn)換處理。
String remotePath = "/upload/qjnn.apk"; //FTP服務(wù)器上文件路徑

String charSet = "UTF-8";
if (!FTPReply.isPositiveCompletion(client.sendCommand("OPTS UTF8", "ON"))) {  //向服務(wù)器請求使用"UTF-8"編碼
  charSet = "GBK";
}
FTPFile[] files = client.listFiles(new String(remotePath.getBytes(charSet), "ISO-8859-1")); //對remotePath進行編碼轉(zhuǎn)換
FTPFile file = files[0]; //文件信息
long size = file.getSize();
String fileaName = new String(fileName.getBytes(), Charset.forName(charSet));

通過以上代碼,便可以獲取到正確的文件信息。

文件下載

配置每條線程的下載區(qū)間

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) { //該線程已經(jīng)完成
  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, "任務(wù)【" + mEntity.getFileName() + "】線程__" + i + "__恢復下載");
  startL = r;
  recordL[rl] = i;
  rl++;
 } else {
  recordL[rl] = i;
  rl++;
 }
 //最后一個線程的結(jié)束位置即為文件的總長度
 if (i == (mThreadNum - 1)) endL = fileLength;
 //創(chuàng)建分段線程
 AbsThreadTask task = createSingThreadTask(i, startL, endL, fileLength);
 if (task == null) return;
 mTask.put(i, task);
}
startSingleTask(recordL);

在上面的代碼中,主要做了兩步操作:

  • 在文件下載前,先從本地文件中讀取當前下載的每一條線程的下載情況
  • 如果下載記錄存在,從記錄位置開始下載,如果記錄不存在,則重新開始下載

FTP 分段線程區(qū)間自動停止

由于FTP協(xié)議沒有區(qū)間下載的原因,為了讓線程只下載特定區(qū)間的內(nèi)容,需要客戶端在單條線程累計讀的數(shù)據(jù)長度已經(jīng)超過了所分配的區(qū)間長度的時候,停止該條線程。

 client.enterLocalPassiveMode();  //設(shè)置被動模式
 client.setFileType(FTP.BINARY_FILE_TYPE); //設(shè)置文件傳輸模式
 client.setRestartOffset(mConfig.START_LOCATION);  //設(shè)置恢復下載的位置
 client.allocate(mBufSize);
 is = client.retrieveFileStream(new String(remotePath.getBytes(charSet), SERVER_CHARSET));
 //發(fā)送第二次指令時,還需要再做一次判斷
 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) { 
  //如果該條線程讀取的數(shù)據(jù)長度大于所分配的區(qū)間長度,則只能讀到區(qū)間的最大長度
  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客戶端來說,一般需要設(shè)置被動模式,被動模式和主動模式的區(qū)別
  2. 在獲取文件流后,還需要使用FTPReply.isPositivePreliminary(reply)進行第二次命令判斷

關(guān)于FTP文件上傳

FTP 文件斷點續(xù)傳的方式原理和下載的都差不多:

  1. 都是在停止的時候記錄停止位置,重新開始下載的時候從指定位置通過REST命令恢復斷點。
  2. 都需要在任務(wù)執(zhí)行前獲取文件信息,比對服務(wù)器上的文件。

而和下載有區(qū)別的是:

  1. FTP上傳時需要指定工作目錄、在遠程服務(wù)器上創(chuàng)建文件夾
  2. 需要服務(wù)器給用戶打開刪除和讀入IO的權(quán)限,否則會出現(xiàn)550權(quán)限錯誤問題
  3. 上傳文件需要storeFileStream獲取outputStream流

最終效果



以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Android獲取文字高度的三種方法

    Android獲取文字高度的三種方法

    這篇文章主要給大家介紹了Android獲取文字高度的三種方法,文中通過圖文介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-01-01
  • Android環(huán)形進度條(安卓默認形式)實例代碼

    Android環(huán)形進度條(安卓默認形式)實例代碼

    這篇文章主要介紹了Android環(huán)形進度條(安卓默認形式)實例代碼的相關(guān)資料,需要的朋友可以參考下
    2016-03-03
  • Android自定義View實現(xiàn)球形動態(tài)加速球

    Android自定義View實現(xiàn)球形動態(tài)加速球

    這篇文章主要為大家詳細介紹了Android自定義View實現(xiàn)球形動態(tài)加速球,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • ProtoBuf動態(tài)拆分Gradle?Module解析

    ProtoBuf動態(tài)拆分Gradle?Module解析

    這篇文章主要為大家介紹了ProtoBuf動態(tài)拆分Gradle?Module解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-02-02
  • Android對圖片Drawable實現(xiàn)變色示例代碼

    Android對圖片Drawable實現(xiàn)變色示例代碼

    這篇文章主要給大家介紹了關(guān)于Android對圖片Drawable實現(xiàn)變色的相關(guān)資料,文中通過示例代碼將實現(xiàn)的方法介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面來一起看看吧。
    2017-08-08
  • Android自定義圖片選擇器簡單版

    Android自定義圖片選擇器簡單版

    這篇文章主要為大家詳細介紹了Android自定義圖片選擇器簡單版,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-02-02
  • Android開發(fā)系列三之窗口的常用事件

    Android開發(fā)系列三之窗口的常用事件

    這篇文章主要介紹了Android開發(fā)系列三之窗口的常用事件的相關(guān)資料,需要的朋友可以參考下
    2016-05-05
  • Android編程實現(xiàn)自定義輸入法功能示例【輸入密碼時防止第三方竊取】

    Android編程實現(xiàn)自定義輸入法功能示例【輸入密碼時防止第三方竊取】

    這篇文章主要介紹了Android編程實現(xiàn)自定義輸入法功能,可實習輸入密碼時防止第三方竊取的效果,結(jié)合實例形式詳細分析了Android布局、控件及輸入法相關(guān)操作技巧,需要的朋友可以參考下
    2017-01-01
  • Android實現(xiàn)帶節(jié)點的進度條

    Android實現(xiàn)帶節(jié)點的進度條

    這篇文章主要為大家詳細介紹了Android實現(xiàn)帶節(jié)點的進度條,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • Android Studio工程導入及坑的解決

    Android Studio工程導入及坑的解決

    本文主要介紹了Android Studio工程導入及坑的解決,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05

最新評論

湛江市| 武邑县| 大安市| 镇坪县| 田阳县| 博白县| 石城县| 曲麻莱县| 河北省| 张掖市| 射阳县| 巴东县| 东山县| 微博| 宾阳县| 崇义县| 天峨县| 广汉市| 延边| 珲春市| 福清市| 陆河县| 兴义市| 绥棱县| 南昌市| 南通市| 抚宁县| 交口县| 岑溪市| 根河市| 施秉县| 新泰市| 安化县| 沁源县| 斗六市| 玛沁县| 新郑市| 永年县| 蒙城县| 同仁县| 黄骅市|