NodeJS中createWriteStream实例遮蔽另一实例的原因与解决方法
问题分析与解决
为什么第一个流会遮蔽第二个流?
核心有两个原因:
- 写流的异步特性:
fs.createWriteStream的写入操作是异步的,w.write()只是把数据放到内存缓冲区,真正写入磁盘是后台异步完成的。你同步调用writer1和writer2,两个流会同时抢占同一个文件的写入权限,操作系统无法保证写入顺序,导致内容互相覆盖。 - 'w'标志的截断行为:使用
flags:'w'打开文件时,只要成功打开就会立即清空文件内容,不管这个文件当前有没有其他流在操作。当writer2打开文件时,会先截断文件,但此时writer1的缓冲区数据还在陆续写入磁盘,最终writer1的内容会覆盖writer2的写入结果。
如何避免这种情况?
关键是串行化写操作,确保前一个写流的所有数据都完全写入磁盘(触发finish事件)后,再执行下一个写流。同时正确判断文件是否存在,只在需要时执行writer1。
修正后的代码示例
const fs = require('fs').promises; const { createWriteStream } = require('fs'); // 将writer1改为返回Promise,等待写入完成 function writer1(data, file) { return new Promise((resolve, reject) => { const w = createWriteStream(file, { flags: 'w' }); for (let i = 0; i < data.length; i++) { // 处理缓冲区满的情况,避免数据丢失 if (!w.write(data[i])) { w.once('drain', () => {}); } } w.end(); w.on('finish', resolve); // 所有数据写入完成后触发 w.on('error', reject); }); } // 同样改造writer2 function writer2(data, file, excludeStr) { return new Promise((resolve, reject) => { const w = createWriteStream(file, { flags: 'w' }); for (let i = 0; i < data.length; i++) { if (data[i] !== excludeStr) { if (!w.write(data[i])) { w.once('drain', () => {}); } } } w.end(); w.on('finish', resolve); w.on('error', reject); }); } // 主逻辑:先判断文件是否存在,再串行执行操作 async function processData(data, file, excludeStr) { let fileExists = false; try { await fs.access(file); // 检查文件是否存在 fileExists = true; } catch { fileExists = false; } // 仅文件不存在时执行writer1,等待完成后再执行writer2 if (!fileExists) { await writer1(data, file); } await writer2(data, file, excludeStr); } // 调用示例 processData(data, "file.txt", "some string").catch(err => console.error('写入失败:', err));
补充说明
- 改造后的函数通过Promise封装,利用
async/await实现串行执行,彻底避免了并发写入的冲突。 - 增加了
drain事件的处理,防止因缓冲区满导致的数据丢失(原代码中如果数据量较大,可能会丢失部分数据)。
内容的提问来源于stack exchange,提问作者AVALFINN
相关产品推荐
相关产品推荐

