如何为stream创建cache或bucket,解决等待文件大小后上传流的write after end错误
报错根因
你代码中触发Error [ERR_STREAM_WRITE_AFTER_END]的核心原因是:http下载流在创建完成后会立刻开始接收响应数据,在await getFileSize的等待过程中,下载流可能已经完成数据接收并自动销毁,后续调用上传方法时操作已结束的流无法再写入/传递数据就会触发该错误。
方案1:调整请求顺序(最优,无额外开销)
如果你的getFileSize是通过HEAD请求获取Content-Length头(不会实际下载文件内容),优先调整执行顺序,先拿文件大小再创建下载流即可:
(async () => { // 第一步先获取文件大小 const filesize = await getFileSize('https://www.google.com/m3u8.mp4'); // 拿到大小后再创建下载流,直接传入上传SDK const video = http('https://www.google.com/m3u8.mp4'); uploadToCOS(video, filesize); })();
方案2:PassThrough流内存缓存(小文件适用)
如果业务逻辑要求必须先创建下载流再获取文件大小,可以使用Node.js内置的stream.PassThrough做中转缓存,它是双工流,会将写入的数据暂存在内部缓冲区,直到有消费者接入后再输出数据:
const { PassThrough } = require('stream'); (async () => { // 创建中转缓存流 const cacheStream = new PassThrough(); // 下载流直接pipe到缓存流,数据会暂存在缓冲区 const video = http('https://www.google.com/m3u8.mp4'); video.pipe(cacheStream); // 等待获取文件大小 const filesize = await getFileSize('https://www.google.com/m3u8.mp4'); // 直接将缓存流传给上传SDK即可 uploadToCOS(cacheStream, filesize); })();
注意:该方案仅适合小文件场景,大文件会占用大量内存,甚至触发OOM。
方案3:临时文件缓存(大文件适用)
大文件场景下建议将下载流先写入本地临时文件,拿到文件大小后再读取临时文件流上传,上传完成后删除临时文件即可:
const fs = require('fs'); const path = require('path'); (async () => { const tmpFilePath = path.join(__dirname, 'tmp_cache.mp4'); // 先将下载流写入本地临时文件 const video = http('https://www.google.com/m3u8.mp4'); const writeStream = fs.createWriteStream(tmpFilePath); video.pipe(writeStream); // 等待临时文件写入完成,同时获取文件大小 const filesize = await new Promise((resolve, reject) => { writeStream.on('finish', () => resolve(fs.statSync(tmpFilePath).size)); writeStream.on('error', reject); }); // 读取临时文件流上传,完成后删除临时文件 const readStream = fs.createReadStream(tmpFilePath); uploadToCOS(readStream, filesize, () => { fs.unlinkSync(tmpFilePath); }); })();
内容的提问来源于stack exchange,提问作者Aero Wang
相关产品推荐
相关产品推荐

