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))
我试图在解压缩时读取 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))