头图

【高心星出品】

RCP框架实现断点续传

RCP框架有提供downloadToStream方法来实现下载文件,并将文件字节转化成流。结合设置请求头的range属性就可以实现断点续传的功能。

在这里插入图片描述

断点续传原理

如果要下载的网络资源比较大,我们可以将网络资源字节分割成多份,通过设置请求头的range字段来控制下载哪一份资源,然后将下载后的资源通过流写入单个文件就可以实现断点续传下载文件。

表示头500个字节:Range: bytes=0-499
表示第二个500字节:Range: bytes=500-999
表示最后500个字节:Range: bytes=-500
表示500字节以后的范围:Range: bytes=500-
第一个和最后一个字节:Range: bytes=0-0,-1
同时指定几个范围:Range: bytes=500-600,601-999

除此之外还需要获得网络资源的总大小,这里可以通过RCP框架中Session对象的head方法去读取网络资源的信息,主要读取content-length字段,该字段返回的是网络资源的字节个数。

let getres = await this.session.head(url) //只获取网络资源应答头
this.filetotal = Number.parseInt(getres.headers?.['content-length'] as string) //获取应答头的资源长度

代码:

import { rcp } from '@kit.RemoteCommunicationKit';
import { fileIo } from '@kit.CoreFileKit';
import { promptAction } from '@kit.ArkUI';

@Entry
@Component
struct Index {
  // 请求头中返回资源字节的开始位置和结束位置
  @State range: string = ''
  // 当前已经下载的字节个数
  @State count: number = 0
  // 要下载的文件字节总个数
  @State filetotal: number = 0
  // 进度条显示进度值
  @State value: number = 0
  // 是否为第一次点击按钮下载
  @State isstart: boolean = true
  // 下载文件的会话对象
  private session: rcp.Session | null = null
  start = async (event: ClickEvent) => { //开始按钮
    let url = 'https://dldir1.qq.com/qqfile/qq/QQNT/Windows/QQ_9.9.15_240927_x64_01.exe'
    // 请求头中加入资源截取位置
    let sessionconfig: rcp.SessionConfiguration = {
      headers: {
        range: this.range
      }
    }
    this.session = rcp.createSession(sessionconfig)
    //创建文件
    let file =
      fileIo.openSync(getContext(this).filesDir + '/QQ.exe', fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE|fileIo.OpenMode.APPEND)

    let downloadstream: rcp.DownloadToStream = {
      kind: 'stream',
      stream: {
        writeSync: (buffer: ArrayBuffer) => { //每次获取网络资源然后写入文件
          let len = fileIo.writeSync(file.fd, buffer) //写入文件
          this.count += len //更新字节总个数
          this.value = this.count*100 / this.filetotal //更新进度值
          return len
        }
      }
    }
    if(this.isstart) { //第一次点击开始按钮获取网络资源总大小
      let getres = await this.session.head(url) //只获取网络资源应答头
      this.filetotal = Number.parseInt(getres.headers?.['content-length'] as string) //获取应答头的资源长度
    }
    this.session.downloadToStream(url, downloadstream)
      .then((res) => { //下载结束之后
        this.value = 100
        promptAction.showToast({message:'下载完毕'})
      })
      .catch((e: Error) => { //如果强制关闭session会调用异常处理
        this.range = `bytes=${this.count}-${this.filetotal}` //更新rang值 在这里能确保this.count是准确已经下载文件资源的字节个数
        console.error('gxxt ', e.message)
      })
      .finally(() => {
        this.session?.close()
      })
  }
  stop = (event: ClickEvent) => {//暂停按钮
    this.isstart = false //将第一次点击设置为false
    this.session?.close() //强制关闭会话对象,后面会引起捕获异常
  }


  build() {
    Column() {
      Progress({ value: this.value, total: 100, type: ProgressType.Capsule }).width('100%').height(20).color(Color.Red)
      Row() {
        Button('开始下载')
          .width('45%')
          .onClick(this.start)
        Button('暂停下载')
          .width('45%')
          .onClick(this.stop)
      }.width('100%').justifyContent(FlexAlign.Center).margin({ top: 20 })
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
  aboutToAppear(): void {
    // 如果要保存文件位置已经有文件了,就删除
    if(fileIo.accessSync(getContext(this).filesDir + '/QQ.exe'))
    {
      fileIo.unlinkSync(getContext(this).filesDir + '/QQ.exe')
    }
  }
}

高心星
1 声望1 粉丝

华为开发者专家(HDE)。 10年教学经验,兼任多家科技公司技术顾问。先后从事JavaEE项目开发、Python爬虫、HarmonyOS移动应用开发等课程的教学工作。参与开发《鸿蒙应用开发基础》和《鸿蒙项目实战》等课程。