Android,Android FTP 多線程斷點續傳下載\上傳的實例

6
回復
2407
查看
打印 上一主題 下一主題
[復制鏈接]

451

主題

1188

帖子

1942

安幣

手工藝人

樓主
發表于 2017-10-25 15:36:11 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式

        

        分段下載

        和HTTP所不同的是,FTP并沒有提供文件區間的API,因此,FTP在分段下載中,只有起始位置而沒有結束位置。
因此,你需要在指定位置手動停止線程。

        功能實現

        本文使用將采用apache commons-net實現FTP斷點續傳下載\上傳功能。<br>

        通過下文的幾步操作,你就能很簡單的實現FTP斷點續傳。

        登錄

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

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

[Java] 查看源文件 復制代碼
FTPClient client = new FTPClient();
client.connect(serverIp, port); //連接到FTP服務器
client.login(userName, passsword);

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

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

[Java] 查看源文件 復制代碼
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. 如果路徑是文件,只會返回一個長度為1的數組。


  

    2. 如果該路徑為文件夾,則會返回該文件夾下對應的所有文件。

[Java] 查看源文件 復制代碼
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,因此,客戶端在獲取文件信息時



  

    1. 需要請求服務器使用UTF-8編碼(如果服務器支持的話),如果服務器不支持開啟UTF-8編碼,那么客戶端需要指定字符串編碼格式


  

    2. 客戶端在請求remotePath路徑、獲取文件名時,都需要對路徑進行編碼轉換處理。

[Java] 查看源文件 復制代碼
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));

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

        文件下載

        配置每條線程的下載區間

[Java] 查看源文件 復制代碼
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. 如果下載記錄存在,從記錄位置開始下載,如果記錄不存在,則重新開始下載

        FTP 分段線程區間自動停止

        由于FTP協議沒有區間下載的原因,為了讓線程只下載特定區間的內容,需要客戶端在單條線程累計讀的數據長度已經超過了所分配的區間長度的時候,停止該條線程。

[Java] 查看源文件 復制代碼
 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 文件斷點續傳的方式原理和下載的都差不多:



  

    3. 都是在停止的時候記錄停止位置,重新開始下載的時候從指定位置通過REST命令恢復斷點。


  

    4. 都需要在任務執行前獲取文件信息,比對服務器上的文件。

        而和下載有區別的是:



  

    5. FTP上傳時需要指定工作目錄、在遠程服務器上創建文件夾


  

    6. 需要服務器給用戶打開刪除和讀入IO的權限,否則會出現550權限錯誤問題


  

    7. 上傳文件需要storeFileStream獲取outputStream流

        最終效果

        


        



分享到:  QQ好友和群 QQ空間 微信
收藏
收藏0
支持
支持0
反對
反對0

6

主題

9578

帖子

2865

安幣

Android大神

Rank: 6Rank: 6

沙發
發表于 2017-10-26 20:48:47 | 只看該作者
幫幫頂頂!!

14

主題

1萬

帖子

3889

安幣

碼皇(巴士元老)

Rank: 8Rank: 8

板凳
發表于 2017-10-28 23:29:39 | 只看該作者
支持樓主,支持安卓巴士!

0

主題

6

帖子

71

安幣

程序猿

Rank: 2

地板
發表于 2017-11-24 15:09:48 | 只看該作者
有源碼嗎??急求

0

主題

6

帖子

71

安幣

程序猿

Rank: 2

5#
發表于 2017-11-24 15:10:35 | 只看該作者
有源碼嗎??急求 [email protected]

4

主題

30

帖子

8

安幣

初級碼農

Rank: 1

QQ達人

6#
發表于 2018-10-28 17:43:40 | 只看該作者
很給力,安卓巴士有你更精彩!

0

主題

2

帖子

950

安幣

代碼手工藝人

Rank: 4

7#
發表于 2019-11-26 16:11:48 | 只看該作者
感謝分享,安卓巴士有你更精彩:)
您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

領先的中文移動開發者社區
18620764416
7*24全天服務
意見反饋:[email protected]

掃一掃關注我們

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粵ICP備15117877號 )

重庆时时彩开奖模拟器 河北11选5任选码走势图 自动赚钱宝 一天 快乐十分软件 上海时时乐组三技巧 北京时时开奖号码结果 南通棋牌大厅app下载 全民红中麻将一元代理 北京pk10冠军3码精准计划 3d走势图带连线专业版 四川麻将上下分代理 手游圣斗士星矢如何赚钱 江西多乐彩计划