如何将Node.js spawn子进程的stdout作为Fastify API流式响应返回
你当前代码的核心问题是将所有子进程输出缓存到变量中,等到子进程退出后才一次性返回,要实现实时逐段返回需要直接将子进程的输出流和HTTP响应流绑定,逐块写入响应。
修改后的完整代码
const fastify = require("fastify")({ logger: true }); const { spawn } = require("child_process"); const bat = require.resolve("./run.bat"); // 声明路由 fastify.post("/", (request, reply) => { const { agent, sub, location, times } = request.body; if (!agent) { return reply.code(400).send({ msg: "缺少必填参数agent" }); } // 设置流式响应头 reply.header("Content-Type", "text/plain; charset=utf-8"); reply.header("Cache-Control", "no-cache"); reply.header("Connection", "keep-alive"); reply.header("Transfer-Encoding", "chunked"); // Windows环境运行bat建议加shell: true参数,避免执行报错 const ls = spawn(bat, [agent, sub, location, times], { shell: process.platform === 'win32' }); ls.stdout.setEncoding("utf8"); ls.stdout.on("data", (data) => { console.log("stdout: " + data); // 实时写入响应流 reply.write(data.toString()); }); ls.stderr.setEncoding("utf8"); ls.stderr.on("data", (data) => { console.log("stderr: " + data); // 错误输出也实时写入响应 reply.write(data.toString()); }); ls.on("exit", (code) => { console.log("子进程退出,退出码: " + code); // 结束响应 reply.end(); }); // 监听子进程错误,避免服务崩溃 ls.on("error", (err) => { console.error("子进程执行出错: ", err); reply.write(`执行错误: ${err.message}\n`); reply.end(); }); }); // 启动服务 const start = async () => { try { await fastify.listen(3000, "0.0.0.0"); } catch (error) { fastify.log.error(error); process.exit(1); } }; start();
关键修改说明
- 移除了路由handler的async修饰符,采用手动控制响应流的模式,避免Fastify自动等待返回值提前结束响应
- 预先设置流式响应头,告知客户端该响应为分块传输,不要等待全部内容加载完成再展示
- 子进程的stdout、stderr收到数据时直接写入响应流,不需要缓存到变量
- 子进程退出或报错时调用
reply.end()主动结束响应,代替原来的一次性reply.send - 新增子进程错误监听,避免执行异常时服务崩溃,同时补充了参数缺失时的响应逻辑
- 新增Windows平台兼容配置,运行bat时自动开启shell模式,避免系统找不到bat执行程序的问题
测试提示
如果使用Postman测试默认会等待全部响应完成再展示内容,可以用curl命令直接测试,即可看到实时逐段输出的效果:curl http://localhost:3000 -X POST -H "Content-Type: application/json" -d '{"agent":"test","sub":"test","location":"test","times":"1"}'
内容的提问来源于stack exchange,提问作者user1559737
相关产品推荐
相关产品推荐

