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

常见场景和解决方案

场景1:低带宽网络上传琐碎文件场景

在网络连接较差,低带宽的网络环境中,HTTP连接的建立耗时可能会大幅提升。这时候进行数据压缩可以加快页面加载速度,并减少HTTP请求数量和移动数据流量。

场景2:处理大量资源的场景

如应用商店、网盘应用等,这类应用通常拥有大体积的文件资源。当用户从暂停或者断网中重新恢复时,如果从头开始上传下载则会额外耗费大量的时间。此时可以采用断点续传方法进行上传下载。

(一)数据压缩

数据压缩是指在应用中对数据进行压缩,以减少存储空间和数据传输量、节省带宽,提高加载速度。数据压缩通常在网络传输和存储方面发挥着重要作用,特别是在处理大量数据或需要频繁传输数据的场景下。

在应用开发中,常见的数据压缩技术分类如下:

有损压缩:仅限图片视频音频等文件适用。通过减少图片视频文件的分辨率,降低音频的音质等手段,以减少文件的大小,来实现减少加载时间和带宽消耗。

无损压缩:对一些零碎文件可以使用 zlib(Zip模块)来进行打包压缩,减少上传请求次数;对一些大文件可以利用缓存技术,服务器将曾经上传过的大文件MD5码缓存起来,本地在上传前预生成MD5码并传输到服务器进行比对,如果相同则说明服务器存在该文件,可以跳过该文件上传,从而省略重复传输时间。

以批量上传照片(分辨率为480*640,24位,平均大小50~120KB)为例,在设备上测试的结果如下表所示:

图2 上传数量和耗时对比图表

由于上传耗时受到网络状态影响,偏差较大,结果取的几次测量结果的最小值。但是仍然可以从数据中看出,优化前的耗时基本为线性增长,压缩优化后的耗时在上传文件数量较低时并不明显,还会因为多余的压缩处理影响耗时。不过随着上传的照片数量增多,优化后的耗时和优化之前的耗时差距越来越明显,优化效果越好。

数据压缩的相关示例代码如下:

导入相关模块:

复制代码
import { common } from '@kit.AbilityKit';
import { fileIo } from '@kit.CoreFileKit'
import { zlib } from '@kit.BasicServicesKit';

创建压缩上传相关类:

复制代码
class ZipUpload {
  // 创建任务前存放的uri
  private waitList: Array<string> = [];
  // 需要上传的文件uri
  private fileUris: Array<string> = [];
  ...
}

建立用于接收图库图片的临时文件夹,并将整个临时文件夹打包添加到待上传list内:

复制代码
// 文件压缩处理
async zipUploadFiles(fileUris: Array<string>): Promise<void> {
  this.context = getContext(this) as common.UIAbilityContext;
  let cacheDir = this.context.cacheDir;
  let tempDir = fileIo.mkdtempSync(`${cacheDir}/XXXXXX`);
  // 将图库图片获取的uri放入fileUris中,遍历复制到临时文件夹
  for (let i = 0; i < fileUris.length; i++) {
    let fileName = fileUris[i].split('/').pop();
    let resourceFile: fileIo.File = fileIo.openSync(fileUris[i], fileIo.OpenMode.READ_ONLY);
    fileIo.copyFileSync(resourceFile.fd, `${tempDir}/${fileName}`, 0);
    fileIo.closeSync(resourceFile);
  }
  // 文件压缩,将之前生成的临时文件夹内打包到test.zip内
  let options: zlib.Options = {
    level: zlib.CompressLevel.COMPRESS_LEVEL_DEFAULT_COMPRESSION,
    memLevel: zlib.MemLevel.MEM_LEVEL_DEFAULT,
    strategy: zlib.CompressStrategy.COMPRESS_STRATEGY_DEFAULT_STRATEGY
  };
  let data = await zlib.compressFile(tempDir, `${cacheDir}/test.zip`, options);
  // 删除临时文件夹
  fileIo.rmdirSync(tempDir);
  // 将生成的zip包放到传输队列
  this.waitList.push(`${cacheDir}/test.zip`);
}

(二)断点续传

断点续传功能的实现,不管是应用端还是服务器端都需要用到合理的技术来互相协同。在实际开发中,开发者无需亲自实现断点续传功能,只需对SDK进行合理配置。

在应用端需要用到的技术和API:

fileIo(文件管理):用于处理文件上传操作,提供了读取文件内容,文件分片和组合的功能。

hash(文件哈希处理):用于实现文件MD5的计算,将计算的MD5值预先传到服务器端进行预处理,实现文件秒传,同时确保传输的准确性和可靠性。

request(上传下载):用于实现文件上传操作,并支持在上传过程中的断点续传功能。

在服务器端需要用到的技术:

协议需要支持Range:用于在服务器端支持范围请求,方便处理文件上传下载断点续传功能。

文件校验相关逻辑:需要实现校验文件是否有错,确保在传输中断后能够准确恢复并继续传输。

通过结合应用端和服务器端的相关技术,可以共同实现高效且可靠的文件断点续传功能,提供更好的用户体验并确保数据传输的稳定性。

本文基于上传和下载中的后台上传场景,给出了部分断点续传的示例代码,具体可以参考该工程。

(三)文件上传

对于大文件断点续传上传,本文采用request(上传下载)模块中的request.agent任务托管接口,可以自动实现暂停继续重试等操作,无需手动将文件分片和记录上传分片信息。流程图如下图所示:

图3 断点续传上传流程图

导入相关模块:

复制代码
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();
}

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

相关推荐
lxyker5 小时前
MongoDB大数据量的优化——mongoTemplate.stream()方法使用
java·数据库·mongodb·性能优化·数据库调优
搞不懂语言的程序员6 小时前
Elasticsearch 写入性能优化有哪些常见手段?
大数据·elasticsearch·性能优化
非著名架构师13 小时前
2025版 JavaScript性能优化实战指南从入门到精通
开发语言·javascript·性能优化
Uranus^14 小时前
深入解析Spring Boot与Redis集成:高效缓存与性能优化
java·spring boot·redis·缓存·性能优化
NoneCoder1 天前
JavaScript 性能优化:调优策略与工具使用
前端·javascript·面试·性能优化
tmacfrank1 天前
Android 性能优化入门(二)—— 内存优化
android·性能优化
qq_214225871 天前
深入解析 Java GC 调优:减少 Minor GC 频率,优化系统吞吐
java·jvm·其他·性能优化
John_ToDebug1 天前
chrome源码中WeakPtr 跨线程使用详解:原理、风险与最佳实践
c++·chrome·性能优化
码观天工2 天前
C#线程池核心技术:从原理到高效调优的实用指南
性能优化·c#·.net·线程·线程池·多线程·异步