GUNzipping 部分文件时如何避免 zlib "unexpected end of file"?

How can I avoid zlib "unexpected end of file" when GUnzipping partial files?

我试图在解压缩时读取 gzip 文件的一部分,这样我就可以解析 header 内容而不读取不必要的字节。我之前使用 fs.read() 进行过此工作,同时传递选项以仅读取前 500 个字节,然后使用 zlib.gunzip() 解压缩内容,然后再从二进制数据中解析 header。

在节点 v5.0.0 修补了一个错误以确保 zlib 在截断的输入上抛出错误之前,这一直工作正常 (https://github.com/nodejs/node/pull/2595)。

现在我从 zlib 收到以下错误。

Error: unexpected end of file

知道我正在截断输入而不会引发错误,如何解压缩这个部分文件。我在想使用流可能会更容易,所以我写了以下内容。

var readStream = fs.createReadStream(file.path, {start: 0, end: 500});
var gunzip = zlib.createGunzip();

readStream.pipe(gunzip)
    .on('data', function(chunk) {
        console.log(parseBinaryHeader(chunk));
        console.log('got %d bytes of data', chunk.length);
    })
    .on('error', function (err) {
        console.log(err);
    })
    .on('end', function() {
        console.log('end');
    });

我的 parseBinaryHeader() 函数正在返回正确的 header 内容,所以我知道它正在解压缩,但是当它到达输入末尾时仍然抛出错误。我可以添加错误侦听器来处理错误而不对其执行任何操作,但这似乎并不理想。

有什么想法吗?

感谢所有建议。我还向节点存储库提交了一个问题并得到了一些很好的反馈。这就是最终对我有用的东西。

  • 将块大小设置为完整 header 大小。
  • 将单个块写入解压缩流并立即暂停流。
  • 处理解压的块。

例子

var bytesRead = 500;
var decompressStream = zlib.createGunzip()
    .on('data', function (chunk) {
        parseHeader(chunk);
        decompressStream.pause();
    }).on('error', function(err) {
        handleGunzipError(err, file, chunk);
    });

fs.createReadStream(file.path, {start: 0, end: bytesRead, chunkSize: bytesRead + 1})
    .on('data', function (chunk) {
        decompressStream.write(chunk);
    });

到目前为止这一直有效,并且还允许我继续处理所有其他 gunzip 错误,因为 pause() 防止解压缩流抛出 "unexpected end of file" 错误。

我 运行 在尝试结束 NodeJS Gzip 流的处理时遇到了同样的问题。我使用“buffer-peek-stream”检查 gzip 流的 header - 确定它实际上是一个 gzip 流。然后我打开流的前几兆字节 - 以查看该文件内部并确定 gzip 内容的 mime 类型。

这需要两次调用 zlib.createGunzip()

我发现,即使我创建了 gunzip t运行sform 的两个独立实例,销毁第二个实例也会导致第一个实例抛出“文件意外结束”错误。即使第一个实例处于完全不同的上下文中。

我的解决方法是在第一个实例上调用 .destroy() 来清理它,然后再创建第二个实例。

我在使用节点 v10.13.0 时遇到此错误。我升级到v10.19.0,修复了。

Constellates 的答案很有效,但前提是可提取块小于 zlib 的处理块大小(默认为 16 KB)。对于更大的数量,您需要组合块,例如通过连接它们。这是一个使用 Promises 的 TS 示例:

const gunzipped: Buffer = await new Promise((resolve, reject) => {
    const buffer_builder: Buffer[] = []
    const decompress_stream = zlib.createGunzip()
        .on('data', (chunk: Buffer) => {
            buffer_builder.push(chunk)
        }).on('close', () => {
            resolve(Buffer.concat(buffer_builder))
        }).on('error', (err) => {
            if(err.errno !== -5) // EOF: expected
                reject(err)
        });
    decompress_stream.write(/* ... your gzipped input buffer */)
    decompress_stream.end()
})

我 运行 在将 net.Socket 管道输送到减压 zlib.createInflate() 流时进入此。

const net = require('net')
const zlib = require('zlib')

net.connect(port, host)
  .pipe(zlib.createInflate())
  .on('data', chunk => console.log('chunk', chunk))

当套接字关闭时,zlib 流将抛出意外的文件结束错误,因为它以部分状态结束。

我通过设置 finishFlush: zlib.constants.Z_SYNC_FLUSH 修复了它,以便它刷新部分数据而不是期望用 Z_FINISH eof 密封完整块。

const net = require('net')
const zlib = require('zlib')

net.connect(port, host)
  .pipe(zlib.createInflate({
    finishFlush: zlib.constants.Z_SYNC_FLUSH,
  }))
  .on('data', chunk => console.log('chunk', chunk))