HarmonyOS NEXT 应用开发中,压缩与解压是常见的需求,例如资源包部署、文件传输、缓存管理等场景。系统提供了基于 zlib 的原生 API,涵盖文件和内存缓冲区的压缩解压能力,同时也支持通过 Worker 线程或三方库扩展功能。以下从基础 API、核心场景、性能优化及常见问题维度展开。


一、 核心 API 概览

HarmonyOS NEXT 的压缩解压能力主要来自 @kit.BasicServicesKit 中的 zlib 模块。

API 功能说明 适用场景
compressFile 压缩单个文件为 .zip 格式 压缩沙箱中的单个文件(如日志、配置)
decompressFile 解压 .zip 格式文件到指定目录 解压离线包、资源包到沙箱
compress / uncompress 对 ArrayBuffer 进行压缩/解压 内存数据处理、网络传输前后压缩
deflate / inflate 流式压缩/解压(未知大小数据) 处理大文件或流式数据,避免内存溢出

除系统 API 外,开发者也可以引入三方库(如 pakojszip)来支持 gzipdeflate 等更多格式。


二、 基础操作:文件压缩与解压

文件压缩和解压是最常用的操作,核心 API 是 compressFile 和 decompressFile

1. 压缩文件

将沙箱中的 data.txt 压缩为 data.zip。关键代码如下:

typescript

import { zlib, BusinessError } from '@kit.BasicServicesKit';

// 获取应用沙箱路径
let context = getContext(this);
let path = context.filesDir;

let inFile = path + '/data.txt';   // 待压缩文件
let outFile = path + '/data.zip';  // 输出压缩包
let options: zlib.Options = {};    // 可配置压缩等级等参数

try {
  zlib.compressFile(inFile, outFile, options).then(() => {
    console.info('文件压缩成功');
  }).catch((err: BusinessError) => {
    console.error(`压缩失败: ${err.code}, ${err.message}`);
  });
} catch (err) {
  // 处理同步异常
}
2. 解压文件

将压缩包解压到指定目录,注意解压前需确保目标目录已存在

typescript

let zipFile = path + '/data.zip';
let targetDir = path + '/unzipped/'; // 解压目标目录

// 确保目标目录存在(如不存在则创建)
if (!fs.accessSync(targetDir)) {
  fs.mkdirSync(targetDir);
}

try {
  zlib.decompressFile(zipFile, targetDir, options).then(() => {
    console.info('文件解压成功');
    // 可选:解压成功后删除原压缩包释放空间
    fs.unlinkSync(zipFile);
  }).catch((err: BusinessError) => {
    console.error(`解压失败: ${err.code}, ${err.message}`);
  });
} catch (err) {
  // 处理异常
}

注意:所有文件路径必须为应用沙箱路径(如通过 context.filesDir 获取),应用无法直接操作外部存储或 rawfile 目录。


三、 核心场景实践

1. 解压 rawfile 中的资源

rawfile 目录下的文件没有沙箱路径,无法直接被 zlib 操作。需要先拷贝到沙箱,再进行解压。

typescript

import { resourceManager } from '@kit.LocalizationKit';

// 1. 读取 rawfile 中的压缩包
let resMgr = getContext(this).resourceManager;
let rawFileData = await resMgr.getRawFileContent('assets.zip');

// 2. 写入沙箱
let sandboxPath = getContext(this).filesDir + '/assets.zip';
let file = fs.openSync(sandboxPath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
fs.writeSync(file.fd, rawFileData.buffer);
fs.closeSync(file);

// 3. 解压到沙箱目标目录
let targetPath = getContext(this).filesDir + '/assets/';
// 创建目标目录...
zlib.decompressFile(sandboxPath, targetPath, {});
2. 结合网络下载实现离线包更新

常见场景:应用启动时检查更新,下载新离线包并解压替换。

typescript

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

async function updateResources() {
  let context = getContext(this);
  let zipPath = context.filesDir + '/new_package.zip';
  let unzipPath = context.filesDir + '/web/';

  // 1. 下载新包
  let downloadTask = await request.downloadFile(context, {
    url: 'https://your-server.com/package.zip',
    filePath: zipPath
  });

  downloadTask.on('complete', () => {
    // 2. 解压下载的包
    zlib.decompressFile(zipPath, unzipPath, {}, (err) => {
      if (!err) {
        console.info('离线包更新成功');
        fs.unlinkSync(zipPath); // 删除压缩包
        // 3. 更新UI或重启WebView加载新资源
      }
    });
  });
}

此场景需在 module.json5 中声明 网络权限 ohos.permission.INTERNET

3. 内存缓冲区压缩(处理 ArrayBuffer

当数据已在内存中(如网络响应、数据库读取),可直接操作 ArrayBuffer,无需落盘。

typescript

// 压缩内存数据
let zip = zlib.createZipSync();
let sourceBuf = new ArrayBuffer(1024); // 假设这是待压缩数据
let sourceLen = sourceBuf.byteLength;

let maxDestLen = await zip.compressBound(sourceLen);
let destBuf = new ArrayBuffer(maxDestLen);

let result = await zip.compress(destBuf, sourceBuf, sourceLen);
// result.destLen 为实际压缩后大小,可按需截取或使用

解压时需提前知道原始数据大小,以便分配目标缓冲区。

typescript

let originalSize = 1024; // 需在压缩前保存原始大小
let compressedBuf = ...; // 读取到的压缩数据
let outBuf = new ArrayBuffer(originalSize);

let result = await zip.uncompress(outBuf, compressedBuf, compressedBuf.byteLength);
// 解压后的数据在 outBuf 中

四、 性能优化与最佳实践

1. 大文件/耗时操作放到 Worker 线程

压缩或解压大文件(如超过 10MB)可能阻塞 UI。官方示例推荐使用 Worker 子线程处理。

  • 主线程:创建 Worker 实例,发送文件路径。

  • Worker 线程:接收数据,执行 zlib.decompressFile,完成后将结果回传主线程。

typescript

// 主线程
let workerInstance = new worker.ThreadWorker('@decompressFile/ets/workers/Worker.ets');
workerInstance.postMessage({ pathDir: this.pathDir, zipName: 'large.zip' });
workerInstance.onmessage = (e) => {
  console.info('解压完成:' + e.data);
};
2. 流式压缩(处理超大文件)

对于超大文件或流式数据,使用 deflate / inflate 分块处理,避免一次性加载到内存。

typescript

// 初始化压缩流
let strm = zlib.createZStream();
// 循环读取数据块,调用 deflate 逐块压缩
// 参考官方示例中的 deflateFile 实现
3. 压缩等级与内存策略

Options 参数可平衡压缩率与速度:

typescript

let options: zlib.Options = {
  level: zlib.CompressLevel.COMPRESS_LEVEL_BEST_SPEED, // 速度优先
  memLevel: zlib.MemLevel.MEM_LEVEL_DEFAULT,            // 默认内存
  strategy: zlib.CompressStrategy.COMPRESS_STRATEGY_DEFAULT_STRATEGY
};
参数 可选值 说明
level COMPRESS_LEVEL_BEST_SPEED / BEST_COMPRESSION / DEFAULT_COMPRESSION 速度 vs 压缩率
memLevel 1~9 内存使用量,越高压缩越快
strategy FILTERED / HUFFMAN_ONLY / DEFAULT_STRATEGY 针对特定数据类型的策略

五、 注意事项与常见问题

  1. 目录必须存在:解压时,目标目录不存在会导致失败。使用 fs.mkdirSync(targetDir, { recursive: true }) 递归创建。

  2. 沙箱路径限制:操作路径必须在应用沙箱内(filesDircacheDir 等),无权限操作 rawfile 或外部公共目录。

  3. 权限声明:涉及网络下载需 ohos.permission.INTERNET;如需保存到媒体库,需 READ_MEDIA / WRITE_MEDIA 权限。

  4. 格式支持:系统 zlib 原生支持 ZIP 格式,gzip 需通过 native 侧 zlib 库或三方库 pako 实现。

  5. 内存管理:大文件压缩建议使用流式 API 或 Worker,避免主线程卡顿或内存溢出。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐