HarmonyOS鸿蒙Next中使用zlib解压buffer,如何动态设置目标缓冲区的数据长度
HarmonyOS鸿蒙Next中使用zlib解压buffer,如何动态设置目标缓冲区的数据长度
【问题现象】
使用zlib进行解压,在数据解压完成前无法提前的目标缓冲区需要的空间大小,所以目标缓冲区需要提前预设值,而目标缓冲区完成设置后,如果初始化长度不够,将会导致代码抛出异常。目标缓冲区的空间是否可以动态增加,边解压边增加目标缓冲区的空间呢?
【背景知识】
【定位思路】
- 压缩的ArrayBuff转成内部流的方式进行解压。
- 将解压的流,循环每次只转换为一个固定大小的ArrayBuffer,并将此ArrayBuffer作为元素添加到一个输出的数组对象,下次循环 以前一次结束位置作为新起点的平移量重新执行下一次转换。
- ArrayBuffer数组对象元素按顺序循环合并为一个最终输出的Uint8Array对象。
- 参考示意图如下:
【解决方案】
1. 核心解压逻辑:
- inBuf:ArrayBuffer转strm:zlib.ZStream,每次的大小为readLen。
- 循环->BUFLEN计算stream的偏移量,作为起点继续下一个截取的stream转换 [循环->(用BUFLEN大小的outBuf:ArrayBuffer缓冲目标区去接收分割后的stream]。
- 判断当前的stream是否为最后的一个,如果是结束循环。
/**
* (1) inBuf:ArrayBuffer转 strm:zlib.ZStream 每次的大小为 let readLen = Math.min(BUFLEN, inBuf.byteLength - offset)
* 循环-> BUFLEN计算stream的偏移量,作为起点继续下一个截取的stream转换 [循环->(用BUFLEN大小的outBuf:ArrayBuffer缓冲目标区去接收分割后的stream]
* (3) 计算stream的偏移量,作为起点继续下一个截取的stream转换
* (4) 判断当前的stream是否为最后的一个,如果是结束循环
*/
async function unzip(inBuf: ArrayBuffer) {
let zip = zlib.createZipSync();
let gzip = zlib.createGZipSync()
// 将压缩后的arrayBuffer 解压的正确姿势
let strm: zlib.ZStream = {};
let BUFLEN = 4096;
let count = inBuf.byteLength / BUFLEN
if (count > 1) {
BUFLEN *= count
}
let outBuf = new ArrayBuffer(BUFLEN);
let output: ArrayBuffer[] = []; // 用于存储解压后的数据
let initStatus = zip.inflateInit(strm);
console.debug('inflateInit ret: ' + (await initStatus).valueOf());
let offset = 0; // 用于跟踪读取的字节数
do {
let readLen = Math.min(BUFLEN, inBuf.byteLength - offset);
if (readLen <= 0) {
break;
}
strm.availableIn = readLen;
strm.nextIn = inBuf.slice(offset, offset + readLen);
offset += readLen; // 更新偏移量
do {
strm.availableOut = BUFLEN;
strm.nextOut = outBuf; // 压缩后的输出字节
try {
let inflateStatus = zip.inflate(strm, zlib.CompressFlushMode.SYNC_FLUSH);
console.debug('inflate ret: ' + (await inflateStatus).valueOf());
let innerStrm = zip.getZStream();
strm.availableIn = (await innerStrm).availableIn;
strm.nextIn = (await innerStrm).nextIn;
strm.availableOut = (await innerStrm).availableOut;
strm.nextOut = (await innerStrm).nextOut;
strm.totalIn = (await innerStrm).totalIn;
strm.totalOut = (await innerStrm).totalOut;
if (strm.availableOut != undefined) {
let have = BUFLEN - strm.availableOut;
if (have > 0) {
// 将解压后的数据存储到 output 数组中
output.push(outBuf.slice(0, have));
}
}
} catch (err) {
console.debug('inflate err: ' + JSON.stringify(err));
}
} while (strm.availableOut == 0);
} while (strm.availableIn! > 0 || strm.availableOut! > 0);
zip.inflateEnd(strm);
// 将 output 数组中的 Uint8Array 合并为一个单一的 Uint8Array
let totalLength = output.reduce((acc, val) => acc + val.byteLength, 0);
// 解压后的buff
let result = new Uint8Array(totalLength);
let pos = 0;
for (let arr of output) {
// 将 ArrayBuffer 转换为 Uint8Array
let u = new Uint8Array(arr);
// 将解压后的数据写入 result
result.set(u, pos);
// 更新位置
pos += u.byteLength;
}
// 转回string检测结果正确
let strData: string = uint8ArrayToString(result)
console.log(strData)
}
2. 压缩对象并输出ArrayBuffer核心代码参考:
function zip(str: string): ArrayBuffer {
let arrayBufferIn = new ArrayBuffer(str.length);
let byteArray = new Uint8Array(arrayBufferIn);
for (let i = 0, j = str.length; i < j; i++) {
byteArray[i] = str.charCodeAt(i)
}
// arrayBufferOut长度必须足够,否则会抛出异常
let arrayBufferOut = new ArrayBuffer(100);
let zip = zlib.createZipSync();
zip.compress2(arrayBufferOut, arrayBufferIn, zlib.CompressLevel.COMPRESS_LEVEL_BEST_SPEED).then((data) => {
console.info('compress2 success');
}).catch((errData: BusinessError) => {
console.error(`errData is errCode:${errData.code} message:${errData.message}`);
})
return arrayBufferOut
}
3. string互转Uint8Array核心代码参考:
/**
* string转Uint8Array
*
*/
function stringToUint8Array(str: string): Uint8Array {
try {
let textEncoder = new util.TextEncoder("utf-8");
let array: Uint8Array = textEncoder.encodeInto(str);
return array
} catch (err) {
console.log(err)
return new Uint8Array()
}
}
/**
* Uint8Array转string
*
*/
function uint8ArrayToString(arr: Uint8Array): string {
let str = ''
if (arr && arr.length > 0) {
try {
let textDecode = util.TextDecoder.create('utf-8')
str = textDecode.decodeWithStream(arr)
} catch (err) {
}
}
return str
}
更多关于HarmonyOS鸿蒙Next中使用zlib解压buffer,如何动态设置目标缓冲区的数据长度的实战教程也可以访问 https://www.itying.com/category-93-b0.html
更多关于HarmonyOS鸿蒙Next中使用zlib解压buffer,如何动态设置目标缓冲区的数据长度的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS鸿蒙Next中使用zlib解压buffer时,动态设置目标缓冲区的数据长度可以通过以下步骤实现:
-
初始化zlib流:使用
inflateInit2
函数初始化zlib流,设置解压缩参数。 -
设置输入缓冲区:将需要解压的数据放入输入缓冲区,并设置
z_stream
结构体中的next_in
和avail_in
字段。 -
动态分配输出缓冲区:根据解压缩数据的预期大小,动态分配输出缓冲区。可以使用
malloc
或calloc
函数进行内存分配。 -
设置输出缓冲区:将分配的输出缓冲区地址赋值给
z_stream
结构体中的next_out
字段,并设置avail_out
字段为缓冲区的大小。 -
解压缩数据:调用
inflate
函数进行解压缩。如果avail_out
为0,表示输出缓冲区已满,需要重新分配更大的缓冲区,并继续解压缩。 -
处理解压缩结果:解压缩完成后,检查
inflate
函数的返回值,确保解压缩成功。如果解压缩成功,输出缓冲区中即为解压后的数据。 -
释放资源:使用
inflateEnd
函数释放zlib流,并释放动态分配的缓冲区。
示例代码片段如下:
#include <zlib.h>
#include <stdlib.h>
void decompressBuffer(const Bytef *src, uLong srcLen, Bytef **dest, uLong *destLen) {
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = srcLen;
strm.next_in = (Bytef *)src;
inflateInit2(&strm, 15 + 32);
*destLen = 4096; // 初始缓冲区大小
*dest = (Bytef *)malloc(*destLen);
strm.avail_out = *destLen;
strm.next_out = *dest;
int ret;
do {
ret = inflate(&strm, Z_NO_FLUSH);
if (ret == Z_OK && strm.avail_out == 0) {
*destLen *= 2;
*dest = (Bytef *)realloc(*dest, *destLen);
strm.next_out = *dest + (*destLen / 2);
strm.avail_out = *destLen / 2;
}
} while (ret == Z_OK);
inflateEnd(&strm);
}
此代码展示了如何动态设置目标缓冲区的数据长度,并在解压缩过程中根据需要调整缓冲区大小。