request-promise流式处理大请求结果及并发下载问题咨询
解决方案:流式下载+并发控制替代方案
嘿,我刚好碰到过类似的问题,来给你捋捋解决方案!确实request-promise因为是基于Promise封装的,天生不支持流式响应处理——它会把整个响应缓冲到内存里再返回,完全没法做pipe操作。下面给你几个靠谱的替代方案,同时结合Bluebird实现最多2个并发的限制:
方案1:原生http/https模块 + Bluebird并发控制
Node.js原生的http/https模块本身就支持流式响应,我们可以把它包装成Promise,再用Bluebird的map方法控制并发数。
先写一个处理单文件流式下载的函数,返回Promise:
const Promise = require('bluebird'); const https = require('https'); const fs = require('fs'); function downloadWithStream(url, outputFilePath) { return new Promise((resolve, reject) => { // 发起请求 const req = https.get(url, (res) => { // 先检查响应状态码 if (res.statusCode < 200 || res.statusCode >= 300) { reject(new Error(`请求失败,状态码:${res.statusCode}`)); return; } // 把响应流pipe到文件流 const fileStream = fs.createWriteStream(outputFilePath); res.pipe(fileStream); // 监听流完成和错误事件 fileStream.on('finish', () => { resolve(`文件 ${outputFilePath} 下载完成`); }); fileStream.on('error', (err) => { reject(new Error(`写入文件出错:${err.message}`)); }); }); // 监听请求本身的错误(比如网络问题) req.on('error', (err) => { reject(new Error(`请求发起失败:${err.message}`)); }); }); }
然后用Bluebird的map方法批量处理URL数组,设置concurrency: 2限制并发数:
// 你的URL数组和对应的输出文件路径 const downloadTasks = [ { url: 'https://example.com/product1', output: 'product1.zip' }, { url: 'https://example.com/product2', output: 'product2.zip' }, { url: 'https://example.com/product3', output: 'product3.zip' }, // 更多任务... ]; Promise.map(downloadTasks, (task) => { return downloadWithStream(task.url, task.output); }, { concurrency: 2 }) .then((results) => { console.log('所有下载任务完成:', results); }) .catch((err) => { console.error('下载过程出错:', err.message); });
方案2:使用got库(更简洁)
如果你不想自己封装原生模块,可以用got这个HTTP客户端——它原生支持Promise和流式响应,而且API更友好。
先安装依赖:
npm install got
然后写代码:
const Promise = require('bluebird'); const got = require('got'); const fs = require('fs'); function downloadWithGot(url, outputFilePath) { // got.stream()返回流,调用.promise()可以把流转化为Promise return got.stream(url).pipe(fs.createWriteStream(outputFilePath)).promise(); } // 同样用Bluebird控制并发 const urls = [ 'https://example.com/product1', 'https://example.com/product2', 'https://example.com/product3' ]; const outputPaths = ['product1.zip', 'product2.zip', 'product3.zip']; Promise.map(urls, (url, index) => { return downloadWithGot(url, outputPaths[index]); }, { concurrency: 2 }) .then(() => { console.log('全部下载完成!'); }) .catch((err) => { console.error('出错了:', err); });
你之前用Bluebird可能踩的坑
如果之前设置concurrency没生效,大概率是这两个原因:
- 你的任务函数没有返回正确的Promise:比如流式处理时没监听
finish/error事件,导致Promise一直处于pending状态,Bluebird没法判断任务是否完成,自然没法控制并发。 - 错误处理不到位:如果某个任务出错但没被捕获,可能会导致整个并发队列卡住。一定要确保每个Promise在出错时会
reject。
内容的提问来源于stack exchange,提问作者PhilG
相关产品推荐
相关产品推荐

