在TypeScript中如何复用fs.ReadStream流实现带重试的上传功能
问题原因
Node.js 中的 fs.ReadStream 是仅可消费一次的流对象,第一次上传完成后,流已经触发 end 事件,内部读取指针已移动到文件末尾,同时流的状态会被标记为已关闭/已结束,无法再次读取数据。你当前的写法仅在循环外创建了一次流实例,后续重试时传入的都是已经消费完的空流,自然无法正常上传。
解决方案
方案1:每次重试时创建新的 ReadStream 实例(推荐,适配所有文件大小)
该方案不需要额外占用内存,每次重试都重新打开文件创建流,是绝大多数场景的最优选择。
for (let i = 0; i < times; i++) { try { const stream = fs.createReadStream(filePath) await upload(stream) // 上传成功直接跳出重试循环 break } catch(e) { console.log(`第${i+1}次上传失败:`, e) // 最后一次重试失败可抛出错误统一处理 if (i === times - 1) { throw new Error(`累计重试${times}次后上传仍失败`) } } }
方案2:小文件场景下先缓存为 Buffer
如果待上传的文件体积很小,你可以提前把文件内容全量读入内存为 Buffer,每次重试时基于 Buffer 创建新的可读流,减少重复的文件 IO 操作。
注意:该方案仅适合体积小于 100MB 的小文件,大文件全量读入内存会导致进程内存占用过高,甚至触发 OOM 崩溃。
const { Readable } = require('stream') // 提前全量读取文件到内存 const fileBuffer = await fs.promises.readFile(filePath) for (let i = 0; i < times; i++) { try { // 每次重试都从Buffer生成新的可读流 const stream = Readable.from(fileBuffer) await upload(stream) break } catch(e) { console.log(`第${i+1}次上传失败:`, e) if (i === times - 1) { throw new Error(`累计重试${times}次后上传仍失败`) } } }
可选高阶方案:自定义可重置流
如果你有特殊需求不想重复打开文件,可以自行封装支持指针重置的流,每次重试时调用 seek(0) 将文件读取指针移到起始位置,同时重置流的状态即可复用。该方案实现复杂度较高,普通业务场景不推荐使用。
内容的提问来源于stack exchange,提问作者Ohad Bitton
相关产品推荐
相关产品推荐

