Node.js循环处理大量Base64数据调用writeFile生成0字节文件问题
问题原因
- 变量作用域问题:循环中使用
var声明所有变量,var属于函数级作用域,不存在块级作用域。7000次循环会在瞬间执行完成,此时所有异步writeFile的回调触发时,拿到的NewGambar、base64Data等变量都是循环最后一次的值,会出现大量文件路径错误、写入内容不匹配的问题,小数据量下循环执行慢于回调触发速度才没有暴露问题。 - 文件描述符耗尽:
fs.writeFile是异步API,一次性调用7000次相当于同时向操作系统申请7000个文件写入句柄,操作系统对单个进程的最大打开文件数有限制(通常默认是1024左右),超过限制的写入请求会直接报错,文件写入中断就会生成0字节的空文件。 - 异步操作无等待逻辑:所有写入操作都是异步触发,主线程不会等待所有写入完成就可能提前退出,部分未完成的写入操作就会生成空文件。
解决方案
优化思路:将模块引入提到循环外避免重复加载,使用fs.promises配合async/await管理异步流程,控制并发数避免耗尽文件描述符,用let/const声明变量利用块级作用域隔离每次循环的参数。
优化后代码如下:
// 模块引入统一放在顶部,避免循环内重复加载 const fs = require('fs/promises') const isBase64 = require('is-base64') const path = require('path') async function saveAllImages(recordset) { const extention = ".jpg" const dir = "image/customer/" // 自动创建目标目录,多层目录不存在也会递归创建,避免目录不存在导致写入失败 await fs.mkdir(path.join(__dirname, '../', dir), { recursive: true }) // 并发数可根据设备配置调整,建议不超过50,避免超出系统文件句柄限制 const concurrency = 10 for (let i = 0; i < recordset.length; i += concurrency) { // 分批处理,每次最多同时处理concurrency个文件 const currentBatch = recordset.slice(i, i + concurrency) // 等待当前批次所有文件写入完成再处理下一批 await Promise.all(currentBatch.map(async (item) => { if (item.Blob == null) return const NewGambar = `${item.CustomerID}_${item.CustomerCode}` const base64Data = Buffer.from(item.Blob).toString('base64') if (!isBase64(base64Data, { allowEmpty: false })) return const filePath = path.join(__dirname, '../', dir, `${NewGambar}${extention}`) await fs.writeFile(filePath, base64Data, 'base64') console.log(`保存成功:${filePath}`) })) } console.log('全部图片处理完成') } // 传入数据调用方法即可 saveAllImages(result.recordset).catch(err => console.error('全局处理错误:', err))
内容的提问来源于stack exchange,提问作者hakim homecent
相关产品推荐
相关产品推荐

