Node.js用zlib和tar-stream解压大tar.gz报ERR_OUT_OF_RANGE如何解决?
问题根因
- 触发ERR_OUT_OF_RANGE的核心原因:你将压缩包内全量文件内容都攒到
chunks数组中,最终调用Buffer.concat生成的单个Buffer超过了Node.js的最大允许长度(32位Node.js最大Buffer长度为2^31-1=2147483647字节,约2GB,你要生成的Buffer大小为3GB左右,直接触发长度超限)。 - 后续Duplex方案文件损坏的原因:一是拼写错误,
bufferToStream的参数名写成了myBuuffer,实际调用的myBuffer是未定义变量,写入内容为空/异常;二是逻辑错误,Buffer.from(chunks)是错误用法,该方法不能直接拼接Buffer数组,还是需要调用Buffer.concat,本质没有解决大Buffer长度超限的问题,最终写入的内容不是合法的sqlite文件结构,所以file命令只能识别为data类型。
纯Node.js解决方案
核心逻辑是流式处理,边解压边写盘,不把全量内容加载到内存,完全规避大Buffer的长度限制:
const fs = require('fs'); const zlib = require('zlib'); const tar = require('tar-stream'); const path = require('path'); const extract = tar.extract(); const gunzip = zlib.createGunzip(); extract.on('entry', function (header, stream, next) { // 拼接文件输出路径,可自行调整输出目录,注意处理路径穿越风险 const destPath = path.resolve('./', header.name); // 直接将当前文件的流pipe到写入流,不需要攒全量chunks stream.pipe(fs.createWriteStream(destPath)) .on('finish', () => { console.log("写入完成: %s", destPath); next(); // 处理下一个文件 }) .on('error', (err) => { console.warn("写入文件失败: %s", err.toString()); next(err); }); }); extract.on('finish', function () { console.log("全部文件解压完成"); }); extract.on('error', (error) => { console.warn("解压失败: %s", error.toString()); }); // 启动流式解压 fs.createReadStream(tmpPath) .pipe(gunzip) .pipe(extract);
如果你的tar包内只有单个sqlite文件,也可以直接使用官方tar包的封装方法,代码更简洁:
const tar = require('tar'); tar.x({ file: tmpPath, C: './', // 指定解压输出目录 gzip: true // 自动处理gz压缩 }).then(() => { console.log('解压成功'); }).catch(err => { console.warn('解压失败: %s', err.toString()); });
该方案完全不需要加载全量文件到内存,支持任意大小的tar包解压,也不会出现文件损坏的问题。
内容的提问来源于stack exchange,提问作者loretoparisi
相关产品推荐
相关产品推荐

