Node.js spawn创建lzip子进程无法获取stdout输出问题排查
问题原因
你当前代码的核心问题是没有等待lzip子进程完成全部处理流程,仅等待了stdin的写入和关闭操作,就直接读取了tarContent的内容。此时lzip可能还在处理输入数据、还没开始向stdout输出解压后的内容,所以data事件还未触发,buffer自然是空的。
修复方案
快速修复原有代码
你只需要新增对lzip子进程生命周期的监听,等进程正常退出后再处理结果即可,同时建议增加错误监听排查问题:
import { requestBinary } from 'by-request'; import { spawn } from 'child_process'; import { min } from '@tubular/math'; export async function tarLzToZip(url: string): Promise<void> { const lzData = await requestBinary(url, { headers: { 'User-Agent': 'curl/7.64.1' } }); const lzipProc = spawn('lzip', ['-d'], { stdio: ['pipe', 'pipe', process.stderr] }); let tarContent = Buffer.alloc(0); // 监听stdout数据 lzipProc.stdout.on('data', data => { tarContent = Buffer.concat([tarContent, data], tarContent.length + data.length); }); // 新增错误监听,方便排查问题 lzipProc.on('error', err => { throw new Error(`lzip进程启动失败: ${err.message}`); }); lzipProc.stderr.on('data', data => { console.error('lzip错误输出:', data.toString()); }); // 写入输入数据 for (let offset = 0; offset < lzData.length; offset += 4096) { await new Promise<void>((resolve, reject) => { lzipProc.stdin.write(lzData.slice(offset, min(offset + 4096, lzData.length)), err => { if (err) reject(err); else resolve(); }); }); } await new Promise<void>((resolve, reject) => { lzipProc.stdin.end((err: any) => { if (err) reject(err); else resolve(); }); }); // 新增:等待lzip进程退出后再返回结果 await new Promise<void>((resolve, reject) => { lzipProc.on('close', code => { if (code === 0) { resolve(); } else { reject(new Error(`lzip进程异常退出,错误码: ${code}`)); } }); }); console.log('data length:', tarContent.length); }
更优方案
你最终采用的流pipe方案是更推荐的实现方式:不需要手动拼接buffer、不需要手动处理分块写入,Node.js的流机制会自动处理背压、数据传输和生命周期管理,性能和稳定性都更高,也更符合Node.js的异步IO设计理念。
需要注意的事项:
- 确保运行环境已经安装了lzip工具,否则spawn会抛出找不到命令的错误
- 处理大文件时,手动拼接buffer会占用大量内存,用流边处理边写入目标文件/输出可以大幅降低内存占用
- 不要忽略子进程的错误监听,避免异常情况无法排查
内容的提问来源于stack exchange,提问作者kshetline
相关产品推荐
相关产品推荐

