使用NodeJs从URL批量下载文件时生成空文件及报错如何解决?
问题根因
- 同步循环瞬间发起大量并发请求、同时创建数百个文件写入流,超出系统允许的同时打开文件数上限,触发
EMFILE报错 - 你用
var声明的file变量是函数作用域,不是块级作用域,所有请求的回调触发时,file已经被循环覆盖为最后一次创建的写入流,导致前面的请求数据都写到了最后一个文件里,其余文件都是空的 - 写的
setTimeout(sleep, 3000)完全不生效:首先你没定义sleep函数,其次定时器是异步的,不会阻塞同步for循环的执行,等于循环还是一瞬间跑完 - 大量并发请求注册的事件监听器超过Node.js默认的事件监听器上限,触发
MaxListenersExceededWarning
解决方法
通过并发控制限制同时运行的下载任务数即可解决所有问题,调整后的可运行代码如下:
const http = require('https'); const fs = require('fs'); const path = require('path'); // 配置项,可根据自身网络和系统情况调整 const MAX_CONCURRENT = 5; // 最大同时下载任务数 const TOTAL_DOWNLOAD = 500; // 总下载数量 const SAVE_DIR = "D:/SavePath/"; // 自动创建保存目录,无需手动提前新建 if (!fs.existsSync(SAVE_DIR)) { fs.mkdirSync(SAVE_DIR, { recursive: true }); } let currentTaskIndex = 1; // 单张图片下载逻辑 async function downloadSingle() { if (currentTaskIndex > TOTAL_DOWNLOAD) return; const taskId = currentTaskIndex++; const random = Math.floor(Math.random() * 1497791); const requestUrl = `https://cdnpath.com${random}.png`; const savePath = path.join(SAVE_DIR, `#${taskId}.png`); return new Promise((resolve, reject) => { const writeStream = fs.createWriteStream(savePath); http.get(requestUrl, (response) => { if (response.statusCode !== 200) { fs.unlink(savePath, () => {}); // 下载失败自动清理空文件 return reject(new Error(`请求失败,状态码:${response.statusCode}`)); } response.pipe(writeStream); writeStream.on('finish', () => { writeStream.close(); console.log(`下载完成:${savePath}`); resolve(); }); }).on('error', (err) => { fs.unlink(savePath, () => {}); // 请求失败自动清理空文件 reject(err); }); }).finally(() => { // 单个任务完成后自动启动下一个任务 return downloadSingle(); }); } // 启动下载 async function run() { // 初始化指定数量的并发任务 const initialTasks = []; for (let i = 0; i < MAX_CONCURRENT; i++) { initialTasks.push(downloadSingle()); } await Promise.all(initialTasks); console.log('全部图片下载完成'); } run().catch(err => console.error('运行异常:', err));
内容的提问来源于stack exchange,提问作者Yangrui
相关产品推荐
相关产品推荐

