鸿蒙实现断点续下

之前的文章中,介绍了使用SFFT实现多线程下载的功能,今天有同学问到了异常退出如何继续下载,这篇介绍一下如何实现断点续下的功能。
实现效果:
实现下载暂停继续,异常退出程序,重新进入可以接续下载。

演示.gif

实现思路:
实现断点续下,只需要我们将下载任务的下载进度记录下来,如果暂停或者异常退出,重新进入下载任务的时候,判断有没有下载的缓存记录,如果有就接续下载,如果没有就重新下载。SFFT已经帮我们实现了缓存记录和读取,我们只需要调用即可。
实现过程:
1.要想实现断点续下,需要在初始化下载任务DownloadTask时,配置DownloadConfig开启断点续下isBreakpointResume

function  getDownloadConfig(url:string,fileName:string,concurrency?:number): DownloadConfig{
  return {
    url: url, // 远端文件url地址
    fileName: fileName+'ssf', // 本地文件名
    concurrency:concurrency!=0?concurrency:1, // 启用的线程数,concurrency为1~8的正整数
    isBreakpointResume: true, // 是否启用断点续下,isBreakpointResume为true时启用
    maxRetries: 3, // 重试次数为3次
    retryInterval: 2000, // 重试间隔为2000ms
  }
}

SFFT源码 DownloadController

//开始下载
public async start() {
  ...
  // 关闭当前已有的下载任务并清除相关信息
  await this.cleanDirtyDownload();
    ...
  // 生成下载任务并存储任务信息和分片信息到数据库与缓存  
    ...
  // 进行文件下载
  this.executeDownload();
}
private executeDownload() {
    ...
    // 启用断点续传的情况下,定时更新各分片下载进度到数据库
    await DownloadInfoManager.getInstance().updateBlockInfos(this.downloadTaskMetadata);
  ...
}

由start方法可以发现,调用start时,会清除之前下载的缓存,重新下载。
SFFT源码 DownloadCacheManager
2.退出重新进入到下载页面时,初始话下载任务后,判断当前任务是否有下载缓存记录。

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

根据下载地址、文件名、存储路径判断当前下载任务是否有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.如果当前下载任务有缓存记录,继续下载,需要调用resume方法

public async resume() {
   //如果没有开启断点续下 直接返回
  //尝试从缓存中匹配下载信息并写入到downloadTask,不存在则直接退出,无法续下
  //初始化下载进度
  //校验链接和参数
  //回调进度
  //开始下载

实现源码

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, // 启用的线程数,concurrency为1~8的正整数
    isBreakpointResume: true, // 是否启用断点续下,isBreakpointResume为true时启用
    maxRetries: 3, // 重试次数为3次
    retryInterval: 2000, // 重试间隔为2000ms
  }
}
export async function initSfft(downloadUrl:string,downloadListener: DownloadListener,concurrency?:number){
  await DownloadManager.getInstance().init(context as common.UIAbilityContext);
  // 根据配置创建下载任务
  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()!
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容