使用 C# 片段压缩的 nodejs 解压缩字符串

Uncompress String with nodejs that was compressed using a C# snippet

我使用 C# 工具收集了一些大型日志信息。因此,我寻找了一种压缩该巨大字符串的方法,并找到了 this 代码段来解决问题:

public static string CompressString(string text)
{
    byte[] buffer = Encoding.UTF8.GetBytes(text);
    var memoryStream = new MemoryStream();
    using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Compress, true))
    {
        gZipStream.Write(buffer, 0, buffer.Length);
    }

    memoryStream.Position = 0;

    var compressedData = new byte[memoryStream.Length];
    memoryStream.Read(compressedData, 0, compressedData.Length);

    var gZipBuffer = new byte[compressedData.Length + 4];
    Buffer.BlockCopy(compressedData, 0, gZipBuffer, 4, compressedData.Length);
    Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gZipBuffer, 0, 4);
    return Convert.ToBase64String(gZipBuffer);
}

在我的记录操作之后,C# 工具将此压缩字符串发送到 node.js REST 接口,该接口将其写入数据库。

现在(以我对压缩的天真理解)我认为我可以简单地使用类似以下代码的东西 在 nodejs 端 来解压缩它:

zlib.gunzip(Buffer.from(compressedLogMessage, 'base64'), function(err, uncompressedLogMessage) {
  if(err) {
    console.error(err);
  }
  else {
    console.log(uncompressedLogMessage.toString('utf-8'));
  }
});

但我收到错误消息:

{ Error: incorrect header check at Zlib._handle.onerror (zlib.js:370:17) errno: -3, code: 'Z_DATA_ERROR' }

好像是压缩方式和解压功能不匹配。我希望任何具有 compression/uncompression 知识的人都可以立即看到问题。

我可以改变或改进什么以使解压缩工作?

非常感谢!

==========更新===========

消息接收和base64解码好像可以.. 使用 CompressString("Hello World") 结果:

// before compression
"Hello World"
// after compression before base64 encoding
new byte[] { 11, 0, 0, 0, 31, 139, 8, 0, 0, 0, 0, 0, 0, 3, 243, 72, 205, 201, 201, 87, 8, 207, 47, 202, 73, 1, 0, 86, 177, 23, 74, 11, 0, 0, 0 }
// after base64 encoding
CwAAAB+LCAAAAAAAAAPzSM3JyVcIzy/KSQEAVrEXSgsAAAA=

在节点 js 端:

// after var buf = Buffer.from('CwAAAB+LCAAAAAAAAAPzSM3JyVcIzy/KSQEAVrEXSgsAAAA=', 'base64');
{"buf":{"type":"Buffer","data":[11,0,0,0,31,139,8,0,0,0,0,0,0,3,243,72,205,201,201,87,8,207,47,202,73,1,0,86,177,23,74,11,0,0,0]}}
// after zlib.gunzip(buf, function(err, dezipped) { ... }

{ Error: incorrect header check at Zlib._handle.onerror (zlib.js:370:17) errno: -3, code: 'Z_DATA_ERROR' }

===============更新 2 ==================

@01binary 回答正确!这是可行的解决方案:

function toArrayBuffer(buffer) {
  var arrayBuffer = new ArrayBuffer(buffer.length);
  var view = new Uint8Array(arrayBuffer);
  for (var i = 0; i < buffer.length; ++i) {
    view[i] = buffer[i];
  }
  return arrayBuffer;
}

// Hello World (compressed with C#) => CwAAAB+LCAAAAAAAAAPzSM3JyVcIzy/KSQEAVrEXSgsAAAA= 

var arrayBuffer = toArrayBuffer(Buffer.from('CwAAAB+LCAAAAAAAAAPzSM3JyVcIzy/KSQEAVrEXSgsAAAA=', 'base64'))
var zlib = require('zlib');
zlib.gunzip(Buffer.from(arrayBuffer, 4), function(err, uncompressedMessage) {
  if(err) {
    console.log(err)
  }
  else {
    console.log(uncompressedMessage.toString()) // Hello World
  }
});

您找到的代码段似乎将 4 个额外字节写入输出流的开头,其中包含原始数据的 "uncompressed" 大小。原作者一定假设接收端的逻辑将要读取这 4 个字节,知道它需要分配那个大小的缓冲区,并将流的其余部分(+4 偏移量)传递给 gunzip。

如果您在节点端使用此签名: https://nodejs.org/api/buffer.html#buffer_class_method_buffer_from_arraybuffer_byteoffset_length

...然后传递字节偏移量 4。gzip 流的前两个字节应该是 { 0x1F, 0x8b },您可以在数组中看到这两个字节从偏移量 4 开始。一个简单的可以在此处找到 zlib header 的示例:

Zlib compression incompatibile C vs C# implementations