鴻蒙實現(xiàn)斷點續(xù)下

之前的文章中,介紹了使用SFFT實現(xiàn)多線程下載的功能,今天有同學(xué)問到了異常退出如何繼續(xù)下載,這篇介紹一下如何實現(xiàn)斷點續(xù)下的功能。
實現(xiàn)效果:
實現(xiàn)下載暫停繼續(xù),異常退出程序,重新進入可以接續(xù)下載。

演示.gif

實現(xiàn)思路:
實現(xiàn)斷點續(xù)下,只需要我們將下載任務(wù)的下載進度記錄下來,如果暫停或者異常退出,重新進入下載任務(wù)的時候,判斷有沒有下載的緩存記錄,如果有就接續(xù)下載,如果沒有就重新下載。SFFT已經(jīng)幫我們實現(xiàn)了緩存記錄和讀取,我們只需要調(diào)用即可。
實現(xiàn)過程:
1.要想實現(xiàn)斷點續(xù)下,需要在初始化下載任務(wù)DownloadTask時,配置DownloadConfig開啟斷點續(xù)下isBreakpointResume

function  getDownloadConfig(url:string,fileName:string,concurrency?:number): DownloadConfig{
  return {
    url: url, // 遠端文件url地址
    fileName: fileName+'ssf', // 本地文件名
    concurrency:concurrency!=0?concurrency:1, // 啟用的線程數(shù),concurrency為1~8的正整數(shù)
    isBreakpointResume: true, // 是否啟用斷點續(xù)下,isBreakpointResume為true時啟用
    maxRetries: 3, // 重試次數(shù)為3次
    retryInterval: 2000, // 重試間隔為2000ms
  }
}

SFFT源碼 DownloadController

//開始下載
public async start() {
  ...
  // 關(guān)閉當前已有的下載任務(wù)并清除相關(guān)信息
  await this.cleanDirtyDownload();
    ...
  // 生成下載任務(wù)并存儲任務(wù)信息和分片信息到數(shù)據(jù)庫與緩存  
    ...
  // 進行文件下載
  this.executeDownload();
}
private executeDownload() {
    ...
    // 啟用斷點續(xù)傳的情況下,定時更新各分片下載進度到數(shù)據(jù)庫
    await DownloadInfoManager.getInstance().updateBlockInfos(this.downloadTaskMetadata);
  ...
}

由start方法可以發(fā)現(xiàn),調(diào)用start時,會清除之前下載的緩存,重新下載。
SFFT源碼 DownloadCacheManager
2.退出重新進入到下載頁面時,初始話下載任務(wù)后,判斷當前任務(wù)是否有下載緩存記錄。

  public getTaskInfoByUrlAndPath(url: string, fileDir: string, fileName: string): DownloadTaskInfo | undefined {
    let taskInfo: DownloadTaskInfo | undefined;
    for (const value of this.taskCache.values()) {
      if (value.url === url && value.fileDir === fileDir && value.fileName === fileName) {
        taskInfo = value;
        break;
      }
    }
    return taskInfo;
  }

根據(jù)下載地址、文件名、存儲路徑判斷當前下載任務(wù)是否有downloadTask信息。
3.通過DownloadController獲取緩存進度

  public async getProgress(): Promise<DownloadProgressInfo> {
    try {
      // 嘗試從緩存中匹配downloadTask信息
      DownloadInfoManager.getInstance().setTaskInfoByCache(this.downloadTaskMetadata);
      return await this.downloadProgress.getDownloadProgressInfo();
    } catch (err) {
      Logger.error(LoggerConstants.DOWNLOAD, `Get progress failed,code: ${err.code}, message: ${err.message}`);
      return {
        transferredSize: 0,
        totalSize: 0,
        speed: 0
      } as DownloadProgressInfo;
    }
  }

4.如果當前下載任務(wù)有緩存記錄,繼續(xù)下載,需要調(diào)用resume方法

public async resume() {
   //如果沒有開啟斷點續(xù)下 直接返回
  //嘗試從緩存中匹配下載信息并寫入到downloadTask,不存在則直接退出,無法續(xù)下
  //初始化下載進度
  //校驗鏈接和參數(shù)
  //回調(diào)進度
  //開始下載

實現(xiàn)源碼

import { rcp } from '@kit.RemoteCommunicationKit';
import { getProgressPercent } from '../utils/CommonUtil';
import { download } from '../net/FileRequest';
import Logger from '../utils/Logger';
import { ProgressBtn } from './ProgressButton';
import { DownloadListener, DownloadProgressInfo } from '@hadss/super_fast_file_trans';
import { getProgress, initSfft, pause, resume, start } from '../net/SFFTRequest';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@ComponentV2
struct RcpDownLoadTest {
  @Local downloadUrl: string =
    'https://cangjie-lang.cn/v1/files/auth/downLoad?nsId=142267&fileName=cangjie-sdk-windows-x64-1.0.3.exe&objectKey=68e724d33115f673ef1280f8';
  @Local downloadProgress: number = 0;
  @Local downloadTime: number = 0;
  @Local downloadtotalSize: string = '';
  @Local ssfProgress: number = 0;
  ssfstarttime: number = 0;
  @Local totalSize: number = 0;
  @Local ssfdownloadtime: number = 0;
  onDownloadProgress: rcp.OnDownloadProgress = (totalSize, downloadedSize) => {
    this.downloadtotalSize = (totalSize / 1024 / 1024).toFixed(2) + 'MB'
    this.downloadProgress = getProgressPercent(totalSize, downloadedSize);
  }
  @Local downloadListener: DownloadListener = {}
  @Local concurrency: number = 1;
  @Local speed: number = 0;
  @Local downloading:boolean = false;
  async aboutToAppear() {
    this.downloadListener = {
      onStart: (trialResponseHeaders: Record<string, string | string[] | undefined>) => {
        this.ssfstarttime = new Date().getTime()
        this.downloading = true
      },
      onSuccess: (filePath: string) => {
        this.ssfdownloadtime = (new Date().getTime() - this.ssfstarttime) / 1000
        this.ssfProgress = 0;
      },
      onProgressUpdate: (downloadProgress: DownloadProgressInfo) => {
        let transferredSize = downloadProgress.transferredSize;
        this.totalSize = downloadProgress.totalSize;
        this.speed = downloadProgress.speed/1024/1024;
        this.ssfProgress = transferredSize / this.totalSize * 100;
      },
      onFail:(err: BusinessError) => {

      },
      onPause:  (downloadProgressInfo: DownloadProgressInfo) => {
        this.downloading = false
      },
      onResume:()=>{
        this.downloading = true
      }
    }
    await initSfft(this.downloadUrl, this.downloadListener, this.concurrency)
    await getProgress().then((value)=>{
      this.totalSize = value.totalSize
      this.speed = value.speed
      this.ssfProgress =this.totalSize==0?0:value.transferredSize / value.totalSize * 100;
    })
  }
  build() {
    Column({ space: 10 }) {
      Text('文件大小:' +  (this.totalSize / 1024 / 1024).toFixed(2) + 'MB  ' + this.speed.toFixed(2)+'MB/S '+ ' 下載耗時:' + this.ssfdownloadtime + 'S').fontSize(18)
      ProgressBtn({
        progress: Number.parseFloat(this.ssfProgress.toFixed(2)) ,
        text: 'SFFT多線程文件下載'
      })
        .margin({ bottom: 10 })
        .onClick( () => {
          if(this.downloading){
            pause();
          }else {
            if (this.ssfProgress==0) {
              start()
            }else {
              resume();
            }
          }
        })
    }
  }
}
---------------------------SFFT初始化--------------------------
import { DownloadConfig, DownloadTask, DownloadManager, DownloadListener,DownloadProgressInfo } from '@hadss/super_fast_file_trans';
import { common } from '@kit.AbilityKit';

const uiContext: UIContext | undefined = AppStorage.get('uiContext');
let context = uiContext!.getHostContext()!;
let downloadInstance: DownloadTask | undefined;
function  getDownloadConfig(url:string,fileName:string,concurrency?:number): DownloadConfig{
  return {
    url: url, // 遠端文件url地址
    fileName: fileName+'ssf', // 本地文件名
    concurrency:concurrency!=0?concurrency:1, // 啟用的線程數(shù),concurrency為1~8的正整數(shù)
    isBreakpointResume: true, // 是否啟用斷點續(xù)下,isBreakpointResume為true時啟用
    maxRetries: 3, // 重試次數(shù)為3次
    retryInterval: 2000, // 重試間隔為2000ms
  }
}
export async function initSfft(downloadUrl:string,downloadListener: DownloadListener,concurrency?:number){
  await DownloadManager.getInstance().init(context as common.UIAbilityContext);
  // 根據(jù)配置創(chuàng)建下載任務(wù)
  downloadInstance = DownloadManager.getInstance().createDownloadTask(getDownloadConfig(downloadUrl,downloadUrl.split('/').pop() || '',concurrency), downloadListener);
}
export async  function start()  {
   await downloadInstance?.start()
}
export async function pause(){
  await downloadInstance?.pause()
}
export async function resume(){
  await downloadInstance?.resume()
}
export async  function getProgress(): Promise<DownloadProgressInfo>{
   return await downloadInstance?.getProgress()!
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容