场景描述

1.基于Zip实现ArrayBuffer类型数据的压缩与解压缩操作:应用开发过程中客户端应用与服务端频繁通信,当消息体积过大时,可以对数据流进行压缩操作,并对收发消息做CRC校验

2.基于Gzip的数据的压缩与解压缩:将日志压缩写入gz文件;解压gz格式html、JSON、图片等各类资源文件。

方案描述

场景一、通信过程中的数据压缩、解压缩与校验

1.效果图

2.方案

1)通过getRawFileContentSync读取rawfile下文件存在arrayBufferIn中;

2)使用zip.compress将源缓冲区arrayBufferIn压缩至目标缓冲区arrayBufferOut;

3)使用socket.constructTCPSocketServerInstance创建TCPSocketServer连接,调用listen绑定IP地址和端口实现监听,通过tcpServer.on('connect')订阅连接,同时订阅TCPSocketConnection连接的接收消息事件,当收到用户端发送的数据后,先用adler32计算Adler-32校验和对传输数据进行校验,再调用zip.uncompress实现数据的解压缩,并将解压缩后的数据通过fs.write写入文件。

4)使用socket.constructTCPSocketInstance创建TCPSocket连接,tcp.bind绑定IP地址和端口,tcp.connect连接到指定IP和端口后计算数据传递前的Adler-32校验和,同时向服务端发送压缩后的目标数据。

3.核心代码

获取resources/rawfile目录下的文件内容

private arrayBufferIn: ArrayBufferLike = getContext().resourceManager.getRawFileContentSync("log.txt").buffer;

压缩实现

async function compressBuffer(arrayBufferIn: ArrayBuffer, arrayBufferOut: ArrayBuffer) {
  let zip = zlib.createZipSync();
  await zip.compress(arrayBufferOut, arrayBufferIn, 12).then((data) => {
    console.info('compress success, data.destLen: ' + data.destLen + ", data.status: " + data.status);
  }).catch((errData: base.BusinessError) => {
    console.error(`errData is errCode:${errData.code}  message:${errData.message}`);
  })
}

解压缩实现

async function deCompressToFile(arrayBufferSource: ArrayBuffer) {
  let arrayBufferDest = new ArrayBuffer(20);
  let zip = zlib.createZipSync();
  await zip.uncompress(arrayBufferDest, arrayBufferSource, 20).then((data) => {
    console.info('uncompress success, data.destLen: ' + data.destLen + ", data.status: " + data.status);
    let filePath = getContext().filesDir + "/mylog1.txt";
    let file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
    let writeLen = fs.writeSync(file.fd, arrayBufferDest);
    fs.closeSync(file);
  }).catch((errData: base.BusinessError) => {
    console.error(`errData is errCode:${errData.code}  message:${errData.message}`);
  })
}

创建TCPSocketServer连接

let tcpServer: socket.TCPSocketServer = socket.constructTCPSocketServerInstance();
// 绑定本地IP地址和端口,进行监听
export function TCPServeListen() {
  let ipAddress: socket.NetAddress = {} as socket.NetAddress;
  ipAddress.address = "127.0.0.1";
  ipAddress.port = 4651;
  tcpServer.listen(ipAddress, (err: BusinessError) => {
    if (err) {
      console.log("Serve testTag-listen fail");
      return;
    }
    console.log("Serve testTag-listen success");
  });
}

订阅TCPSocketServer的连接事件

export function TCPServerConnect() {
  // 订阅TCPSocketServer的connect事件
  tcpServer.on("connect", (client: socket.TCPSocketConnection) => {
    // 订阅TCPSocketConnection相关的事件
    client.on("close", () => {
      console.log("Serve testTag-on close success");
    });
    client.on("message", (value: SocketInfo) => {
      let checksum = zlib.createChecksumSync()
      //计算Adler-32校验和
      checksum.adler32(0, value.message).then(data => {
        console.info('receive Adler-32校验和: ', data);
      })
      //解压数据
      deCompressToFile(value.message)
    });
  });
}

创建TCPSocket连接,并向指定IP地址和端口发送数据。

let tcp: socket.TCPSocket = socket.constructTCPSocketInstance();
// 绑定本地IP地址和端口。
let ipAddress: socket.NetAddress = {} as socket.NetAddress;
ipAddress.address = "127.0.0.1";
ipAddress.port = 1234;
tcp.bind(ipAddress, (err: BusinessError) => {
  if (err) {
    console.log('testTag-bind fail');
    return;
  }
  console.log('testTag-bind success');
  // 连接到指定的IP地址和端口。
  ipAddress.address = "127.0.0.1";
  ipAddress.port = 4651;

  let tcpConnect: socket.TCPConnectOptions = {} as socket.TCPConnectOptions;
  tcpConnect.address = ipAddress;
  tcpConnect.timeout = 6000;

  tcp.connect(tcpConnect, (err: BusinessError) => {
    if (err) {
      console.log('testTag-connect fail');
      return;
    }
    // 发送数据
    let tcpSendOptions: socket.TCPSendOptions = {} as socket.TCPSendOptions;
    tcpSendOptions.data = messag;
    tcp.send(tcpSendOptions, (err: BusinessError) => {
      if (err) {
        console.log('testTag-send fail');
        return;
      }
      console.log('testTag-send success');
    })
    console.log('testTag-connect success');
  });
});

场景二、基于Zip实现大文件的压缩与解压缩

1.效果图

2.方案

1)压缩方案:使用zlib.createZipSync创建压缩对象并初始化压缩流,循环判断当前文件是否已读取压缩完成,如未完成,则继续读取文件,使用zip.deflate压缩到buffer中并继续写入文件;

2)解压缩方案:使用zlib.createZipSync创建解压缩对象并初始化压缩流,循环判断当前文件是否已读取解压完成,如未完成,则继续读取文件,使用zip.inflate继续解压到buffer中并写入解压后的文件中。

3.核心代码

文件压缩实现

export async function deflateFile(src: fs.File, dest: fs.File) {
  let flush = zlib.CompressFlushMode.NO_FLUSH; //默认值,表示正常操作
  let strm: zlib.ZStream = {};
  const BUFLEN = 4096;
  let inBuf = new ArrayBuffer(BUFLEN);
  let outBuf = new ArrayBuffer(BUFLEN);

  let zip = zlib.createZipSync();//创建压缩对象
  let initStatus = zip.deflateInit(strm, zlib.CompressLevel.COMPRESS_LEVEL_BEST_SPEED);
  console.debug('deflateInit ret: ' + (await initStatus).valueOf());
  do {
    let readLen = fs.readSync(src.fd, inBuf);
    console.debug("readSync readLen: " + readLen);
    flush = readLen == 0 ? zlib.CompressFlushMode.FINISH : zlib.CompressFlushMode.NO_FLUSH;
    strm.availableIn = readLen;
    strm.nextIn = inBuf;
    do {
      strm.availableOut = BUFLEN;
      strm.nextOut = outBuf;
      try {
        let deflateStatus = zip.deflate(strm, flush);
        console.debug('deflate ret: ' + (await deflateStatus).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;
          let writeLen = fs.writeSync(dest.fd, outBuf, { length: have });
          console.debug(`writeSync writeLen: ${writeLen}`);
        }
      } catch (err) {
        console.debug('deflate err: ' + JSON.stringify(err));
      }
    } while (strm.availableOut == 0);
  } while (flush != zlib.CompressFlushMode.FINISH);
  zip.deflateEnd(strm);
}

文件解压实现

export async function inflateFile(src: fs.File, dest: fs.File) {
  let status: zlib.ReturnStatus = zlib.ReturnStatus.OK;
  let strm: zlib.ZStream = {};
  const BUFLEN = 4096;
  let inBuf = new ArrayBuffer(BUFLEN);
  let outBuf = new ArrayBuffer(BUFLEN);

  let zip = zlib.createZipSync();//创建压缩对象
  let initStatus = zip.inflateInit(strm);
  console.debug('inflateInit ret: ' + (await initStatus).valueOf());
  do {
    let readLen = fs.readSync(src.fd, inBuf);
    console.debug("readSync readLen: " + readLen);
    if (readLen == 0) {
      break;
    }
    strm.availableIn = readLen;
    strm.nextIn = inBuf;
    do {
      strm.availableOut = BUFLEN;
      strm.nextOut = outBuf;
      try {
        let inflateStatus = zip.inflate(strm, zlib.CompressFlushMode.NO_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;
          let writeLen = fs.writeSync(dest.fd, outBuf, { length: have });
          console.debug(`writeSync writeLen: ${writeLen}`);
        }
      } catch (err) {
        console.debug('inflate err: ' + JSON.stringify(err));
      }
    } while (strm.availableOut == 0)
  } while (status != zlib.ReturnStatus.STREAM_END.valueOf())
  zip.inflateEnd(strm);
}

场景三、基于GZip实现将字符串压缩写入gz文件

1.效果图

2.方案

使用createGZipSync创建GZip对象,通过gzip.gzwrite实现将buffer压缩进gz文件中

3.核心代码

let filePath = getContext().filesDir + "/hilog.gz";
let gzip = zlib.createGZipSync();
await gzip.gzopen(filePath, "wb");
let str = 'gzwrite success.';
let bufferWithData = new ArrayBuffer(str.length);
let uint8View = new Uint8Array(bufferWithData);
for (let i = 0; i < str.length; i++) {
  uint8View[i] = str.charCodeAt(i);
}
let result = await gzip.gzwrite(bufferWithData, str.length);
await gzip.gzclose();

场景四、解压读取html文件并加载

1.效果图

2.方案

1)通过createGZipSync创建GZip对象,使用gzread读取gz文件中并解压到buffer中,然后转为字符串类型;

2)通过webController加载html页面

3.核心代码

1)解压读取gz文件

let filePath = getContext().resourceDir + "/html.gz";
let fileStat = fs.statSync(filePath).size;
let gzip = zlib.createGZipSync();
await gzip.gzopen(filePath, "rb");
let readBuffer = new ArrayBuffer(fileStat);
let result = await gzip.gzread(readBuffer);
await gzip.gzclose();
let textDecoder = util.TextDecoder.create('utf-8');
let uint8 = new Uint8Array(readBuffer);
this.webData = textDecoder.decodeToString(uint8);

2)加载html页面

this.webController.loadData(
  this.webData,
  "text/html",
  "UTF-8"
);

HarmonyOS码上奇行
9.2k 声望3.3k 粉丝

欢迎关注 HarmonyOS 开发者社区:[链接]