如何用Node.js不使用.then语法实现媒体文件跨服务器管道传输
Node.js 流式传输媒体文件方案(从file-server到file-receiver)
方案一:原生http模块 + Async/Await + 流式管道
原生HTTP请求返回的响应本身就是可读流,天然支持管道操作,通过Promise封装配合async/await可完全避免.then链式调用。
const http = require('http'); const { pipeline } = require('stream/promises'); // Node.js 15+ 提供,自动处理流的背压与资源清理 // 从file-server获取文件可读流 async function getFileStream(fileServerUrl) { return new Promise((resolve, reject) => { const req = http.get(fileServerUrl, (res) => { if (res.statusCode !== 200) { reject(new Error(`获取文件失败:${res.statusCode}`)); return; } resolve(res); }); req.on('error', reject); }); } // 上传可读流到file-receiver async function uploadStream(fileStream, receiverUrl) { return new Promise((resolve, reject) => { const uploadReq = http.request(receiverUrl, { method: 'POST', headers: { 'Content-Type': 'application/octet-stream', // 根据实际媒体类型调整 } }, (res) => { res.statusCode >= 200 && res.statusCode < 300 ? resolve(res.statusCode) : reject(new Error(`上传失败:${res.statusCode}`)); }); uploadReq.on('error', reject); // 使用pipeline替代直接pipe,更安全地处理流传输 pipeline(fileStream, uploadReq, (err) => err && reject(err)); }); } // 主流程 async function transferMedia(fileServerUrl, receiverUrl) { try { const fileStream = await getFileStream(fileServerUrl); await uploadStream(fileStream, receiverUrl); console.log('文件传输完成'); } catch (err) { console.error('传输出错:', err); } } // 调用示例 transferMedia('http://file-server/path/to/media', 'http://file-receiver/upload');
方案二:node-fetch + Async/Await + 流式传输
如果习惯使用Fetch API,node-fetch的响应对象body属性是可读流,可转换为Node.js流后进行管道操作。
const fetch = require('node-fetch'); const { pipeline } = require('stream/promises'); const http = require('http'); async function transferMedia(fileServerUrl, receiverUrl) { try { // 从file-server获取流式响应 const fileRes = await fetch(fileServerUrl, { method: 'GET' }); if (!fileRes.ok) throw new Error(`获取文件失败:${fileRes.status}`); // 创建上传请求 const uploadReq = http.request(receiverUrl, { method: 'POST', headers: { 'Content-Type': fileRes.headers.get('content-type') || 'application/octet-stream', 'Content-Length': fileRes.headers.get('content-length') } }); // 执行流式传输 await pipeline(fileRes.body, uploadReq); // 校验上传结果 const uploadRes = await new Promise(resolve => uploadReq.on('response', resolve)); if (!(uploadRes.statusCode >= 200 && uploadRes.statusCode < 300)) { throw new Error(`上传失败:${uploadRes.statusCode}`); } console.log('文件传输完成'); } catch (err) { console.error('传输出错:', err); } } // 调用示例 transferMedia('http://file-server/path/to/media', 'http://file-receiver/upload');
方案三:got库简化流式操作
got库原生支持流式API,代码更简洁,无需手动封装Promise。
const got = require('got'); const { pipeline } = require('stream/promises'); async function transferMedia(fileServerUrl, receiverUrl) { try { // 获取文件可读流 const fileStream = got.stream(fileServerUrl); // 创建上传可写流 const uploadStream = got.stream.post(receiverUrl, { headers: { 'Content-Type': 'application/octet-stream' } }); // 动态获取原文件的Content-Type(可选) fileStream.on('response', res => { uploadStream.setHeader('Content-Type', res.headers['content-type']); }); // 执行传输 await pipeline(fileStream, uploadStream); console.log('文件传输完成'); } catch (err) { console.error('传输出错:', err); } } // 调用示例 transferMedia('http://file-server/path/to/media', 'http://file-receiver/upload');
方案选择建议
- 原生模块方案:无额外依赖,适合轻量场景,兼容性最好
node-fetch方案:适配Fetch API习惯,需额外安装依赖got方案:API简洁,开发效率高,适合快速实现
内容的提问来源于stack exchange,提问作者Joram
相关产品推荐
相关产品推荐

