压缩与解压:HarmonyOS开发中Zip文件处理
压缩与解压:HarmonyOS开发中Zip文件处理
一、小知识
你肯定用过zip文件——下载个开源项目是zip,备份个相册是zip,就连浏览器导出书签都是zip。这东西太常见了,以至于我们很少思考它背后的原理。
但做应用开发时,你迟早会遇到这些场景:
- 用户要导出一堆数据,总不能一个一个文件发吧?
- 日志文件积攒了几百个,占了好几G空间,得压缩归档
- 批量下载资源包,zip格式比一个个文件下载快多了
- 应用内资源太大,打包成zip按需解压加载
这些场景都离不开压缩与解压。HarmonyOS内置了zlib模块,支持标准的zip格式处理,既能压缩单个文件,也能打包整个目录,还能设置压缩级别平衡速度和压缩率。
压缩这事儿看着简单,实际用起来全是细节——路径处理、编码问题、大文件内存溢出、压缩进度显示……咱们这篇就把这些坑都填上。
二、核心原理
2.1 Zip文件格式
Zip文件本质上是个容器,里面包含:
- 本地文件头:每个文件的元信息(文件名、压缩方法、CRC校验等)
- 压缩数据:实际压缩后的文件内容
- 中央目录:文件索引,记录所有文件的偏移量
- 中央目录结束记录:整个zip的元信息
graph TD
subgraph Zip文件结构
A[本地文件头1]:::primary
B[压缩数据1]:::info
C[本地文件头2]:::primary
D[压缩数据2]:::info
E[...]:::warning
F[中央目录]:::success
G[中央目录结束记录]:::danger
end
A --> B --> C --> D --> E --> F --> G
classDef primary fill:#2196F3,stroke:#1976D2,color:#fff
classDef info fill:#00BCD4,stroke:#0097A7,color:#fff
classDef warning fill:#FF9800,stroke:#F57C00,color:#fff
classDef success fill:#4CAF50,stroke:#388E3C,color:#fff
classDef danger fill:#F44336,stroke:#D32F2F,color:#fff
2.2 压缩算法
Zip格式支持多种压缩算法,最常用的是DEFLATE:
- STORE:不压缩,直接存储(适合已压缩的图片、视频)
- DEFLATE:LZ77+Huffman编码,通用性强,压缩率好
- BZIP2:压缩率更高,但速度慢(HarmonyOS暂不支持)
压缩级别从0到9:
- 0:不压缩(STORE)
- 1:最快压缩,压缩率最低
- 6:默认级别,平衡速度和压缩率
- 9:最高压缩率,速度最慢
2.3 压缩解压流程
graph TD
A[压缩请求]:::primary --> B{压缩类型?}:::warning
B -->|单文件| C[读取文件内容]:::info
B -->|目录| D[遍历目录文件]:::info
C --> E[调用zlib压缩]:::success
D --> F[逐个文件压缩]:::success
F --> G[写入Zip中央目录]:::info
E --> H[生成Zip文件]:::danger
G --> H
I[解压请求]:::primary --> J[解析Zip中央目录]:::info
J --> K[读取文件头]:::info
K --> L[调用zlib解压]:::success
L --> M[写入目标路径]:::danger
classDef primary fill:#2196F3,stroke:#1976D2,color:#fff
classDef info fill:#00BCD4,stroke:#0097A7,color:#fff
classDef warning fill:#FF9800,stroke:#F57C00,color:#fff
classDef success fill:#4CAF50,stroke:#388E3C,color:#fff
classDef danger fill:#F44336,stroke:#D32F2F,color:#fff
三、代码实战
3.1 基础用法:单文件压缩解压
先从最简单的场景开始——压缩和解压单个文件:
import zlib from '@ohos.zlib';
import fs from '@ohos.file.fs';
import { Callback } from '@ohos.base';
/**
* 单文件压缩工具
*/
export class FileCompressor {
/**
* 压缩单个文件
* @param srcPath 源文件路径
* @param destZipPath 目标zip文件路径
* @param level 压缩级别 0-9
*/
async compressFile(
srcPath: string,
destZipPath: string,
level: number = 6
): Promise<void> {
// 检查源文件是否存在
if (!fs.accessSync(srcPath)) {
throw new Error(`Source file not found: ${srcPath}`);
}
// 确保目标目录存在
const destDir = destZipPath.substring(0, destZipPath.lastIndexOf('/'));
if (!fs.accessSync(destDir)) {
fs.mkdirSync(destDir, true);
}
// 创建压缩选项
const options: zlib.ZipOptions = {
level: level, // 压缩级别
memLevel: 8, // 内存级别(影响内存占用)
strategy: zlib.CompressStrategy.DEFAULT_STRATEGY
};
// 执行压缩
return new Promise((resolve, reject) => {
zlib.compressFile(srcPath, destZipPath, options, (errCode) => {
if (errCode === 0) {
console.info(`[Compressor] Compressed: ${srcPath} -> ${destZipPath}`);
resolve();
} else {
reject(new Error(`Compress failed with code: ${errCode}`));
}
});
});
}
/**
* 解压单个zip文件
* @param zipPath zip文件路径
* @param destDir 目标目录
*/
async decompressFile(zipPath: string, destDir: string): Promise<void> {
// 检查zip文件是否存在
if (!fs.accessSync(zipPath)) {
throw new Error(`Zip file not found: ${zipPath}`);
}
// 确保目标目录存在
if (!fs.accessSync(destDir)) {
fs.mkdirSync(destDir, true);
}
// 创建解压选项
const options: zlib.UnzipOptions = {
cover: true // 覆盖已存在的文件
};
// 执行解压
return new Promise((resolve, reject) => {
zlib.decompressFile(zipPath, destDir, options, (errCode) => {
if (errCode === 0) {
console.info(`[Compressor] Decompressed: ${zipPath} -> ${destDir}`);
resolve();
} else {
reject(new Error(`Decompress failed with code: ${errCode}`));
}
});
});
}
/**
* 获取压缩统计信息
*/
async getCompressionStats(srcPath: string, zipPath: string): Promise<CompressionStats> {
const srcStat = fs.statSync(srcPath);
const zipStat = fs.statSync(zipPath);
return {
originalSize: srcStat.size,
compressedSize: zipStat.size,
compressionRatio: ((1 - zipStat.size / srcStat.size) * 100).toFixed(2) + '%'
};
}
}
/**
* 压缩统计信息
*/
interface CompressionStats {
originalSize: number;
compressedSize: number;
compressionRatio: string;
}
3.2 进阶用法:目录压缩
实际场景中,更常见的是压缩整个目录:
import zlib from '@ohos.zlib';
import fs from '@ohos.file.fs';
/**
* 目录压缩器
* 支持递归压缩、过滤、进度回调
*/
export class DirectoryCompressor {
private totalFiles: number = 0;
private processedFiles: number = 0;
/**
* 压缩目录
* @param srcDir 源目录
* @param destZipPath 目标zip路径
* @param options 压缩选项
*/
async compressDirectory(
srcDir: string,
destZipPath: string,
options: DirectoryCompressOptions = {}
): Promise<void> {
const {
level = 6,
excludePatterns = [],
includeHidden = false,
onProgress
} = options;
// 收集要压缩的文件列表
const fileList = await this.collectFiles(srcDir, excludePatterns, includeHidden);
this.totalFiles = fileList.length;
this.processedFiles = 0;
if (fileList.length === 0) {
throw new Error('No files to compress');
}
console.info(`[DirCompressor] Total files to compress: ${this.totalFiles}`);
// 创建临时目录存放压缩文件
const tempDir = `${destZipPath}_temp`;
if (!fs.accessSync(tempDir)) {
fs.mkdirSync(tempDir, true);
}
try {
// 逐个文件压缩
for (const fileInfo of fileList) {
const relativePath = fileInfo.path.substring(srcDir.length + 1);
const tempFilePath = `${tempDir}/${relativePath.replace(/\//g, '_')}.zip`;
// 压缩单个文件
await this.compressSingleFile(fileInfo.path, tempFilePath, level);
this.processedFiles++;
if (onProgress) {
onProgress({
current: this.processedFiles,
total: this.totalFiles,
currentFile: relativePath
});
}
}
// 合并所有zip为一个(这里简化处理,实际应使用ZipOutputStream)
// HarmonyOS的zlib模块暂不支持直接创建多文件zip
// 实际项目中建议使用native实现或第三方库
console.info(`[DirCompressor] Compression completed`);
} finally {
// 清理临时文件
this.cleanupTempDir(tempDir);
}
}
/**
* 收集目录下的所有文件
*/
private async collectFiles(
dir: string,
excludePatterns: string[],
includeHidden: boolean
): Promise<FileInfo[]> {
const files: FileInfo[] = [];
await this.walkDirectory(dir, files, excludePatterns, includeHidden);
return files;
}
/**
* 递归遍历目录
*/
private async walkDirectory(
currentDir: string,
fileList: FileInfo[],
excludePatterns: string[],
includeHidden: boolean
): Promise<void> {
const entries = fs.listFileSync(currentDir);
for (const entry of entries) {
const fullPath = `${currentDir}/${entry}`;
const stat = fs.statSync(fullPath);
// 跳过隐藏文件
if (!includeHidden && entry.startsWith('.')) {
continue;
}
// 检查排除模式
if (this.shouldExclude(entry, excludePatterns)) {
continue;
}
if (stat.isDirectory()) {
// 递归处理子目录
await this.walkDirectory(fullPath, fileList, excludePatterns, includeHidden);
} else {
// 添加文件到列表
fileList.push({
path: fullPath,
size: stat.size,
name: entry
});
}
}
}
/**
* 检查是否应该排除
*/
private shouldExclude(fileName: string, patterns: string[]): boolean {
for (const pattern of patterns) {
if (fileName.includes(pattern) || this.matchWildcard(fileName, pattern)) {
return true;
}
}
return false;
}
/**
* 通配符匹配
*/
private matchWildcard(text: string, pattern: string): boolean {
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
return regex.test(text);
}
/**
* 压缩单个文件
*/
private async compressSingleFile(
srcPath: string,
destPath: string,
level: number
): Promise<void> {
return new Promise((resolve, reject) => {
zlib.compressFile(srcPath, destPath, { level }, (errCode) => {
if (errCode === 0) {
resolve();
} else {
reject(new Error(`Compress failed: ${errCode}`));
}
});
});
}
/**
* 清理临时目录
*/
private cleanupTempDir(tempDir: string): void {
if (fs.accessSync(tempDir)) {
const files = fs.listFileSync(tempDir);
for (const file of files) {
fs.unlinkSync(`${tempDir}/${file}`);
}
fs.rmdirSync(tempDir);
}
}
}
/**
* 目录压缩选项
*/
interface DirectoryCompressOptions {
level?: number; // 压缩级别
excludePatterns?: string[]; // 排除模式(如 ['*.tmp', 'node_modules'])
includeHidden?: boolean; // 是否包含隐藏文件
onProgress?: (progress: CompressProgress) => void; // 进度回调
}
/**
* 文件信息
*/
interface FileInfo {
path: string;
size: number;
name: string;
}
/**
* 压缩进度
*/
interface CompressProgress {
current: number;
total: number;
currentFile: string;
}
3.3 实战案例:日志归档
结合压缩实现日志文件的自动归档和清理:
import { FileCompressor } from './FileCompressor';
import fs from '@ohos.file.fs';
/**
* 日志归档器
* 自动压缩旧日志,释放存储空间
*/
export class LogArchiver {
private logDir: string;
private archiveDir: string;
private maxLogAge: number; // 日志保留天数
private compressor: FileCompressor;
constructor(logDir: string, archiveDir: string, maxLogAge: number = 7) {
this.logDir = logDir;
this.archiveDir = archiveDir;
this.maxLogAge = maxLogAge;
this.compressor = new FileCompressor();
}
/**
* 执行归档
* 将超过保留期的日志压缩归档
*/
async archive(): Promise<ArchiveResult> {
const result: ArchiveResult = {
archivedFiles: 0,
savedSpace: 0,
errors: []
};
// 确保归档目录存在
if (!fs.accessSync(this.archiveDir)) {
fs.mkdirSync(this.archiveDir, true);
}
// 获取所有日志文件
const logFiles = fs.listFileSync(this.logDir);
const now = Date.now();
const maxAgeMs = this.maxLogAge * 24 * 60 * 60 * 1000;
for (const logFile of logFiles) {
const logPath = `${this.logDir}/${logFile}`;
// 跳过目录
if (fs.statSync(logPath).isDirectory()) {
continue;
}
// 检查文件年龄
const stat = fs.statSync(logPath);
const fileAge = now - stat.mtime;
if (fileAge > maxAgeMs) {
try {
// 压缩归档
const archiveName = `${logFile}_${this.formatDate(stat.mtime)}.zip`;
const archivePath = `${this.archiveDir}/${archiveName}`;
await this.compressor.compressFile(logPath, archivePath, 9); // 最高压缩
// 删除原文件
fs.unlinkSync(logPath);
result.archivedFiles++;
result.savedSpace += stat.size - fs.statSync(archivePath).size;
console.info(`[LogArchiver] Archived: ${logFile}`);
} catch (error) {
result.errors.push(`Failed to archive ${logFile}: ${error.message}`);
}
}
}
return result;
}
/**
* 清理过期的归档文件
* @param maxArchiveAge 归档保留天数
*/
async cleanupOldArchives(maxArchiveAge: number = 30): Promise<number> {
if (!fs.accessSync(this.archiveDir)) {
return 0;
}
const archives = fs.listFileSync(this.archiveDir);
const now = Date.now();
const maxAgeMs = maxArchiveAge * 24 * 60 * 60 * 1000;
let deletedCount = 0;
for (const archive of archives) {
const archivePath = `${this.archiveDir}/${archive}`;
const stat = fs.statSync(archivePath);
if (now - stat.mtime > maxAgeMs) {
fs.unlinkSync(archivePath);
deletedCount++;
console.info(`[LogArchiver] Deleted old archive: ${archive}`);
}
}
return deletedCount;
}
/**
* 获取存储统计
*/
getStorageStats(): StorageStats {
const stats: StorageStats = {
logDirSize: 0,
archiveDirSize: 0,
logFileCount: 0,
archiveFileCount: 0
};
if (fs.accessSync(this.logDir)) {
const logFiles = fs.listFileSync(this.logDir);
stats.logFileCount = logFiles.length;
for (const file of logFiles) {
stats.logDirSize += fs.statSync(`${this.logDir}/${file}`).size;
}
}
if (fs.accessSync(this.archiveDir)) {
const archives = fs.listFileSync(this.archiveDir);
stats.archiveFileCount = archives.length;
for (const archive of archives) {
stats.archiveDirSize += fs.statSync(`${this.archiveDir}/${archive}`).size;
}
}
return stats;
}
/**
* 格式化日期
*/
private formatDate(timestamp: number): string {
const date = new Date(timestamp);
return `${date.getFullYear()}${(date.getMonth() + 1).toString().padStart(2, '0')}${date.getDate().toString().padStart(2, '0')}`;
}
}
/**
* 归档结果
*/
interface ArchiveResult {
archivedFiles: number;
savedSpace: number;
errors: string[];
}
/**
* 存储统计
*/
interface StorageStats {
logDirSize: number;
archiveDirSize: number;
logFileCount: number;
archiveFileCount: number;
}
3.4 完整UI示例
import { FileCompressor, CompressionStats } from './FileCompressor';
import { LogArchiver, ArchiveResult, StorageStats } from './LogArchiver';
import picker from '@ohos.file.picker';
@Entry
@Component
struct ZipDemoPage {
@State message: string = '压缩解压演示';
@State progress: string = '';
@State stats: StorageStats | null = null;
private compressor: FileCompressor = new FileCompressor();
private archiver: LogArchiver = new LogArchiver(
'/data/service/el2/base/hms/logs',
'/data/service/el2/base/hms/logs/archive'
);
async aboutToAppear(): Promise<void> {
this.updateStats();
}
/**
* 更新统计信息
*/
updateStats(): void {
this.stats = this.archiver.getStorageStats();
}
/**
* 选择文件并压缩
*/
async selectAndCompress(): Promise<void> {
// 创建文件选择器
const documentPicker = new picker.DocumentViewPicker();
try {
const selectResult = await documentPicker.select({
maxSelectNumber: 1
});
if (selectResult && selectResult.length > 0) {
const srcPath = selectResult[0];
const zipPath = `${srcPath}.zip`;
this.progress = '压缩中...';
await this.compressor.compressFile(srcPath, zipPath, 6);
const stats = await this.compressor.getCompressionStats(srcPath, zipPath);
this.message = `压缩完成!\n原始大小: ${this.formatSize(stats.originalSize)}\n压缩后: ${this.formatSize(stats.compressedSize)}\n压缩率: ${stats.compressionRatio}`;
this.progress = '';
}
} catch (error) {
this.message = `压缩失败: ${error.message}`;
this.progress = '';
}
}
/**
* 执行日志归档
*/
async doArchive(): Promise<void> {
this.progress = '归档中...';
try {
const result: ArchiveResult = await this.archiver.archive();
this.message = `归档完成!\n已归档: ${result.archivedFiles} 个文件\n释放空间: ${this.formatSize(result.savedSpace)}`;
if (result.errors.length > 0) {
this.message += `\n错误: ${result.errors.length} 个`;
}
this.updateStats();
} catch (error) {
this.message = `归档失败: ${error.message}`;
} finally {
this.progress = '';
}
}
/**
* 格式化文件大小
*/
formatSize(bytes: number): string {
if (bytes < 1024) {
return `${bytes} B`;
} else if (bytes < 1024 * 1024) {
return `${(bytes / 1024).toFixed(2)} KB`;
} else if (bytes < 1024 * 1024 * 1024) {
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
} else {
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
}
}
build() {
Column() {
// 标题
Text(this.message)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
.textAlign(TextAlign.Center)
// 进度提示
if (this.progress) {
Row() {
LoadingProgress()
.width(20)
.height(20)
Text(this.progress)
.fontSize(14)
.margin({ left: 10 })
}
.margin({ bottom: 20 })
}
// 统计信息
if (this.stats) {
Column() {
Text('存储统计')
.fontSize(16)
.fontWeight(FontWeight.Medium)
.margin({ bottom: 10 })
Row() {
Text(`日志文件: ${this.stats.logFileCount} 个`)
Blank()
Text(this.formatSize(this.stats.logDirSize))
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
Row() {
Text(`归档文件: ${this.stats.archiveFileCount} 个`)
Blank()
Text(this.formatSize(this.stats.archiveDirSize))
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.margin({ top: 5 })
}
.width('90%')
.padding(15)
.backgroundColor('#F0F0F0')
.borderRadius(10)
.margin({ bottom: 20 })
}
// 操作按钮
Button('选择文件压缩')
.width('80%')
.onClick(() => this.selectAndCompress())
Button('执行日志归档')
.width('80%')
.margin({ top: 15 })
.onClick(() => this.doArchive())
Button('清理过期归档')
.width('80%')
.margin({ top: 15 })
.onClick(async () => {
const count = await this.archiver.cleanupOldArchives(30);
this.message = `已清理 ${count} 个过期归档`;
this.updateStats();
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Start)
.padding({ top: 50, left: 20, right: 20 })
}
}
四、踩坑与注意事项
4.1 内存溢出问题
压缩大文件时,如果一次性读入内存可能导致OOM:
// 问题:大文件压缩可能内存溢出
// zlib.compressFile内部会分块处理,但仍有上限
// 解决方案:超大文件分块压缩
async function compressLargeFile(srcPath: string, destPath: string): Promise<void> {
const CHUNK_SIZE = 10 * 1024 * 1024; // 10MB分块
const stat = fs.statSync(srcPath);
if (stat.size > 100 * 1024 * 1024) { // 超过100MB
// 使用流式压缩(需要native实现)
throw new Error('File too large, use streaming compression');
}
// 正常压缩
await compressFile(srcPath, destPath);
}
4.2 文件名编码问题
Zip文件名默认使用系统编码,中文文件名可能乱码:
// 问题:中文文件名乱码
// Zip格式支持UTF-8文件名,但需要正确设置标志位
// HarmonyOS的zlib模块默认使用UTF-8,一般不会有问题
// 但解压第三方创建的zip时可能遇到GBK编码
// 解决方案:尝试多种编码
function detectFileNameEncoding(rawName: string): string {
// 尝试UTF-8解码
try {
return decodeURIComponent(escape(rawName));
} catch {
// 可能是GBK编码,需要额外处理
return rawName; // 降级处理
}
}
4.3 路径遍历攻击
解压恶意zip时,文件路径可能包含../,导致文件被写到预期目录外:
// 危险:直接使用zip中的路径
const destPath = path.join(destDir, entry.fileName); // 可能是 /data/../sensitive
// 安全做法:校验路径
function sanitizePath(baseDir: string, relativePath: string): string {
// 规范化路径
const normalized = path.normalize(path.join(baseDir, relativePath));
// 检查是否在目标目录内
if (!normalized.startsWith(path.normalize(baseDir))) {
throw new Error('Path traversal detected');
}
return normalized;
}
4.4 压缩率与速度权衡
不同类型文件的压缩效果差异巨大:
// 已压缩文件再压缩效果差,反而浪费时间
const alreadyCompressed = ['.jpg', '.png', '.mp4', '.zip', '.gz'];
function shouldCompress(fileName: string): boolean {
const ext = fileName.split('.').pop()?.toLowerCase() ?? '';
return !alreadyCompressed.includes(ext);
}
// 根据文件类型选择压缩策略
function getCompressionLevel(fileName: string): number {
if (!shouldCompress(fileName)) {
return 0; // 不压缩,直接存储
}
// 文本文件压缩效果好,用高压缩率
const textFiles = ['.txt', '.json', '.xml', '.log'];
const ext = fileName.split('.').pop()?.toLowerCase() ?? '';
return textFiles.includes(ext) ? 9 : 6;
}
4.5 并发压缩问题
多个压缩任务同时执行可能导致资源竞争:
// 问题:并发压缩占用大量CPU和内存
const files = [...]; // 100个文件
await Promise.all(files.map(f => compress(f))); // 可能卡死
// 解决方案:限制并发数
async function compressWithLimit(files: string[], limit: number = 4): Promise<void> {
const queue: string[] = [...files];
const workers: Promise<void>[] = [];
for (let i = 0; i < limit; i++) {
workers.push((async () => {
while (queue.length > 0) {
const file = queue.shift();
if (file) {
await compress(file);
}
}
})());
}
await Promise.all(workers);
}
五、HarmonyOS 6适配说明
5.1 API接口变化
HarmonyOS 6对zlib API进行了Promise化改造:
// HarmonyOS 5写法:回调模式
zlib.compressFile(srcPath, destPath, options, (errCode) => {
if (errCode === 0) {
console.info('Compress success');
}
});
// HarmonyOS 6适配:Promise模式
try {
await zlib.compressFile(srcPath, destPath, options);
console.info('Compress success');
} catch (error) {
console.error(`Compress failed: ${error.message}`);
}
5.2 新增流式压缩
HarmonyOS 6支持流式压缩,解决大文件内存问题:
// HarmonyOS 6新增:流式压缩
import zlib from '@ohos.zlib';
async function streamingCompress(srcPath: string, destPath: string): Promise<void> {
// 创建压缩流
const zipStream = zlib.createZipStream({
level: 6,
method: zlib.CompressMethod.DEFLATE
});
// 打开目标文件写入流
const destFile = fs.openSync(destPath, fs.OpenMode.CREATE | fs.OpenMode.WRITE_ONLY);
// 分块读取、压缩、写入
const srcFile = fs.openSync(srcPath, fs.OpenMode.READ_ONLY);
const CHUNK_SIZE = 1024 * 1024; // 1MB
const stat = fs.statSync(srcPath);
for (let offset = 0; offset < stat.size; offset += CHUNK_SIZE) {
const buffer = new ArrayBuffer(CHUNK_SIZE);
const readLen = fs.readSync(srcFile.fd, buffer, { offset: 0, length: CHUNK_SIZE });
// 压缩数据块
const compressed = await zipStream.write(buffer.slice(0, readLen));
// 写入目标文件
fs.writeSync(destFile.fd, compressed);
}
// 完成压缩
const finalData = await zipStream.end();
fs.writeSync(destFile.fd, finalData);
// 关闭文件
fs.closeSync(srcFile);
fs.closeSync(destFile);
}
5.3 压缩选项扩展
HarmonyOS 6新增更多压缩控制选项:
// HarmonyOS 6新增选项
const options: zlib.ZipOptions = {
level: 6,
memLevel: 8,
strategy: zlib.CompressStrategy.DEFAULT_STRATEGY,
// 新增选项
windowBits: 15, // 滑动窗口大小
dictionary: undefined, // 预定义字典(提升特定数据压缩率)
checksum: true, // 是否计算校验和
progress: (current: number, total: number) => {
console.info(`Progress: ${(current / total * 100).toFixed(1)}%`);
}
};
5.4 多线程压缩
HarmonyOS 6支持多线程压缩,提升大文件压缩速度:
// HarmonyOS 6新增:多线程压缩
import zlib from '@ohos.zlib';
const options: zlib.ZipOptions = {
level: 6,
threads: 4, // 使用4个线程并行压缩
chunkSize: 256 * 1024 // 每个线程处理256KB
};
await zlib.compressFile(largeFilePath, destPath, options);
六、总结
| 维度 | 评价 |
|---|---|
| 学习难度 | ⭐⭐⭐ |
| 使用频率 | ⭐⭐⭐⭐ |
| 重要程度 | ⭐⭐⭐⭐ |
| 调试难度 | ⭐⭐ |
压缩解压是数据管理的必备技能。通过本文的学习,你应该掌握了:
核心收获:
- Zip格式原理:文件结构、压缩算法、压缩级别选择
- 基础操作:单文件压缩解压、目录压缩、进度监控
- 实战应用:日志归档、批量导出、存储优化
- 安全防护:路径遍历检测、编码处理、并发控制
最佳实践建议:
- 根据文件类型选择压缩级别,已压缩文件用STORE模式
- 大文件使用流式压缩,避免内存溢出
- 解压前校验文件路径,防止路径遍历攻击
- 限制并发压缩任务数,避免资源竞争
- 定期清理过期归档,释放存储空间
压缩不是万能的——对于已经高度压缩的图片、视频文件,再压缩反而可能增大体积。记住:只压缩那些"压缩收益高"的文件,比如文本、日志、JSON数据等。
- 点赞
- 收藏
- 关注作者
评论(0)