【HarmonyOS NEXT】鸿蒙应用压缩和解压处理
HarmonyOS NEXT 应用开发中,压缩与解压是常见的需求,例如资源包部署、文件传输、缓存管理等场景。系统提供了基于 zlib 的原生 API,涵盖文件和内存缓冲区的压缩解压能力,同时也支持通过 Worker 线程或三方库扩展功能。以下从基础 API、核心场景、性能优化及常见问题维度展开。
一、 核心 API 概览
HarmonyOS NEXT 的压缩解压能力主要来自 @kit.BasicServicesKit 中的 zlib 模块。
| API | 功能说明 | 适用场景 |
|---|---|---|
compressFile |
压缩单个文件为 .zip 格式 |
压缩沙箱中的单个文件(如日志、配置) |
decompressFile |
解压 .zip 格式文件到指定目录 |
解压离线包、资源包到沙箱 |
compress / uncompress |
对 ArrayBuffer 进行压缩/解压 |
内存数据处理、网络传输前后压缩 |
deflate / inflate |
流式压缩/解压(未知大小数据) | 处理大文件或流式数据,避免内存溢出 |
除系统 API 外,开发者也可以引入三方库(如 pako、jszip)来支持 gzip、deflate 等更多格式。
二、 基础操作:文件压缩与解压
文件压缩和解压是最常用的操作,核心 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 |
针对特定数据类型的策略 |
五、 注意事项与常见问题
-
目录必须存在:解压时,目标目录不存在会导致失败。使用
fs.mkdirSync(targetDir, { recursive: true })递归创建。 -
沙箱路径限制:操作路径必须在应用沙箱内(
filesDir、cacheDir等),无权限操作 rawfile 或外部公共目录。 -
权限声明:涉及网络下载需
ohos.permission.INTERNET;如需保存到媒体库,需READ_MEDIA/WRITE_MEDIA权限。 -
格式支持:系统
zlib原生支持 ZIP 格式,gzip 需通过 native 侧zlib库或三方库pako实现。 -
内存管理:大文件压缩建议使用流式 API 或 Worker,避免主线程卡顿或内存溢出。
更多推荐




所有评论(0)