HarmonyOS优化应用文件上传下载慢问题性能优化三

鸿蒙时代
发布于 2025-5-28 14:52
浏览
0收藏

(一)文件上传
对于大文件断点续传上传,本文采用request(上传下载)模块中的request.agent任务托管接口,可以自动实现暂停继续重试等操作,无需手动将文件分片和记录上传分片信息。流程图如下图所示:
图3 断点续传上传流程图
HarmonyOS优化应用文件上传下载慢问题性能优化三-鸿蒙开发者社区
导入相关模块:

import { common } from '@kit.AbilityKit';
import { request } from '@kit.BasicServicesKit';

创建相关上传类:

 class Upload {
  // 后台任务
  private backgroundTask: request.agent.Task | undefined = undefined;
  // 创建任务前存放的uri
  private waitList: Array<string> = [];
  ...
}

配置Config,创建后台上传任务:

private config: request.agent.Config = {
  action: request.agent.Action.UPLOAD,
  headers: HEADER,
  url: '',
  mode: request.agent.Mode.BACKGROUND,
  method: 'POST',
  title: 'upload',
  network: request.agent.Network.ANY,
  data: [],
  token: 'UPLOAD_TOKEN'
}
...
// 转换uri
private async getFilesAndData(cacheDir: string, fileUris: Array<string>): Promise<Array<request.agent.FormItem>> {
...
}
// 创建文件上传后台任务
async createBackgroundTask(fileUris: Array<string>) {
  if (this.context === undefined) {
    return;
  }
  // 获取上传url
  this.config.url = await urlUtils.getUrl(this.context);
  this.config.data = await this.getFilesAndData(this.context.cacheDir, fileUris);
  this.config.mode = request.agent.Mode.BACKGROUND;
  try {
    this.backgroundTask = await request.agent.create(this.context, this.config);
    await this.backgroundTask.start();
    let state = AppStorage.get<number>('backTaskState');
    if (state === BackgroundTaskState.PAUSE) {
      await this.backgroundTask.pause();
    }
    logger.info(TAG, `createBackgroundTask success`);
  } catch (err) {
    logger.error(TAG, `task  err, err  = ${JSON.stringify(err)}`);
  }
}

任务开始:

await this.backgroundTask.start();

任务暂停:

async pause() {
  ...
  if (this.backgroundTask === undefined) {
    return;
  }
  await this.backgroundTask.pause();
}

任务继续:

async resume() {
  ...
  if (this.backgroundTask === undefined) {
    return;
  }
  await this.backgroundTask.resume();
}

(二)文件下载
对于大文件断点续传下载,也可以直接调用request.agent接口,该接口的断点续传是基于HTTP协议Header里的Range字段实现的,在任务暂停重启的时候,会自动设置Header中的Range字段,无需进行额外的配置。

Range简介
HTTP协议里面的Range字段,官方名称为范围请求,它允许服务器只发送 HTTP 消息的一部分到客户端,可以用来请求部分数据而不是整个资源。
Range的格式通常是Range: <unit>=<start>-<end>,其中<unit>表示范围所采用的单位,通常是字节(bytes),<start> 和 <end> 表示请求的起始字节和结束字节的位置。
Range语法如下:

// 表示从range-start到文件末尾
Range: <unit>=<range-start>-
// 表示从range-start到range-end
Range: <unit>=<range-start>-<range-end>
// 可以同时选择多段,用逗号分隔
Range: <unit>=<range-start>-<range-end>, <range-start>-<range-end>


// 示例:表示返回1024btyes之后的文件
Range: bytes=1024-

服务器收到请求后,正确处理请求会回复206 Partial Content,未正常处理则会回复其他响应码。下表是服务器回复的常见响应码:
HarmonyOS优化应用文件上传下载慢问题性能优化三-鸿蒙开发者社区
断点续传下载示例代码如下:
导入模块:

import { common } from '@kit.AbilityKit';
import { request } from '@kit.BasicServicesKit';

创建下载类:

class RequestDownload {
  // 任务存放前的uri
  private waitList: Array<string[]> = [];
  // 下载任务
  private downloadTask: request.agent.Task | undefined = undefined;
  ...
}

配置Config,创建后台下载任务:

async createBackgroundTask(downloadList: Array<string[]>) {
  if (this.context === undefined) {
    return;
  }
  for (let i = 0; i < downloadList.length; i++) {
    try {
      let splitUrl = downloadList[i][1].split('//')[1].split('/');
      let downloadConfig: request.agent.Config = {
        action: request.agent.Action.DOWNLOAD,
        url: downloadList[i][1],
        method: 'POST',
        title: 'download',
        mode: request.agent.Mode.BACKGROUND, // 必须是后台任务才能续传
        network: request.agent.Network.ANY,
        saveas: `./${downloadList[i][0]}/${splitUrl[splitUrl.length-1]}`,
        overwrite: true,
        gauge: true
      }
      let downTask = await request.agent.create(this.context, downloadConfig);
      if (this.backgroundDownloadTaskList.findIndex(task => task.config.url === downTask.config.url) === -1) {
        this.backgroundDownloadTaskList.push(downTask);
      }
      await downTask.start();
    } catch (err) {
      logger.error(TAG, `task  err, err  = ${JSON.stringify(err)}`);
      this.waitList.push(downloadList[i]);
    }
  }
}

任务开始:

await downTask.start();

(五)多文件下载监听
文件下载监听是指在单文件下载的功能基础上,同时进行多个文件下载进度和状态的监听管理。实际开发中,需要使用request上传下载模块实现,包括监听每个文件下载任务的进度,任务是否暂停,下载是否完成等状态情况。
以具体场景为例,下图是常见的多文件下载列表:

进入页面后,点击“全部开始”,启动所有文件的下载任务。点击“全部暂停”,暂停所有文件下载任务。再次点击“全部开始”,可重新启动未完成的下载任务。下载完成的文件会保存在应用缓存路径下。如果出现下载失败,一般是网络不稳定,点击“全部开始”可重新下载。
实现思路
配置下载参数。一个下载任务需要对应配置一套下载参数request.agent.Config。本例中使用downloadConfig方法简单配置了下载文件的url,实际业务中按需调整配置。
HarmonyOS优化应用文件上传下载慢问题性能优化三-鸿蒙开发者社区

let config: request.agent.Config = {
  action: request.agent.Action.DOWNLOAD, // 配置任务选项,这里配置为下载任务
  url: downloadUrl, // 配置下载任务url
  overwrite: true, // 下载过程中路径已存在时的解决方案选择。true表示覆盖已存在的文件
  method: 'GET', // HTTP标准方法。下载时,使用GET或POST。
  saveas: './', // 这里'./'表示下载至应用当前缓存路径下。
  mode: request.agent.Mode.BACKGROUND, // 任务模式设置后台任务。
  gauge: true // 后台任务的过程进度通知策略,仅应用于后台任务。true表示发出每个进度已完成或失败的通知。
};

创建多个文件下载监听实例。单个文件下载监听只需要配置下载参数,创建下载任务,注册任务相关监听,启动下载任务即可实现。而要实现多文件下载监听,需要每个下载任务注册独立的下载监听回调。本例通过封装自定义组件FileDownloadItem,在每个FileDownloadItem中创建各自的下载任务和监听回调,从而实现多文件下载监听。

ForEach(this.downloadConfigArray, (item: request.agent.Config) => {
  ListItem() {
    // 创建文件下载监听实例
    FileDownloadItem({
      downloadConfig: item, // 文件下载配置
      isStartAllDownload: this.isStartAllDownload, // 是否全部开始下载
      downloadCount: this.downloadCount // 待下载任务数量
    })
  }
}, (item: request.agent.Config) => JSON.stringify(item))

创建下载任务,并注册下载任务相关监听。本例在每个FileDownloadItem中使用request.agent.create创建下载任务。然后在下载任务创建成功后,注册各自下载任务相关监听。本例中注册了下载任务的完成回调、失败回调、进度更新回调、暂停回调、重新启动回调、response响应头数据回调,在相应的回调中获取当前文件的下载状态等数据。

request.agent.create(context, this.downloadConfig).then((task: request.agent.Task) => {
  // 注册下载任务相关回调
  task.on('completed', this.completedCallback); // 完成回调
  task.on('failed', this.failedCallback); // 失败回调
  task.on('pause', this.pauseCallback); // 暂停回调
  task.on('resume', this.resumeCallback); // 重新启动回调
  task.on('progress', this.progressCallback); // 进度更新回调
  task.on('response', this.progressCallback); // response响应头数据回调
}).catch((err: BusinessError) => {
  logger.error(TAG, `Failed to task create with error message: ${err.message}, error code: ${err.code}`);
});


private completedCallback = (progress: request.agent.Progress) => {
  // 下载状态设置为下载完成
  this.state = "下载完成";
  if (this.sFileSize === '未知大小') {
     this.nCurrentDownloadSize = 1;
  }
  // 文件下载完成,待下载任务数量减1
  this.downloadCount--;
  this.isShow = false;
}

启动下载任务。本例在每个FileDownloadItem中使用task.start方法启动各自的下载任务。此外,start方法也可以启动一个已失败或已停止的下载任务,从上次的进度开始续传。

task.start().then(() => {
  this.downloadTask = task;
}).catch((err: Error) => {
  logger.error(TAG, 'task start error:', err);
})

管理下载任务。在每个FileDownloadItem中,根据对应下载任务的状态,使用task.pause、task.resume方法分别控制单个任务的暂停、恢复。

// 判断当前下载任务状态是否满足暂停条件。
if (this.downloadTask && (taskInfo.progress.state === request.agent.State.WAITING || taskInfo.progress.state
  === request.agent.State.RUNNING || taskInfo.progress.state === request.agent.State.RETRYING)) {
  this.downloadTask.pause().then(() => {
  }).catch((err: Error) => {
    logger.error(TAG, 'task pause error:', err);
  });
}


// 判断如果任务是暂停状态,则重新启动下载任务
if (this.downloadTask && taskInfo.progress.state === request.agent.State.PAUSED) {
  this.downloadTask.resume().then(() => {
  }).catch((err: Error) => {
    logger.error(TAG, 'task resume error:', err);
  });
}

本文主要引用参考HarmonyOS官方文档

分类
标签
收藏
回复
举报
回复
    相关推荐