之前就已经写过一篇 浅谈 js 异步,最近有人问我怎么写批量下载。
在日常开发中,我们经常会遇到需要批量下载文件的场景,比如下载图片集合、备份资源文件等。本文将介绍几种在 Node.js 中实现批量下载文件的方法。
方案概览
实现批量下载有多种方案:
- 使用 request 模块自己封装 Promise 实现批量下载
- 使用封装好的 got 模块直接批量下载
- 使用 axios 等现代 HTTP 客户端库
- 实现并发控制以提高下载效率
需要注意的是,request 模块已经被标记为废弃,建议使用更现代的替代方案。
方案一:使用 request 模块(已废弃,仅供参考)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| const request = require("request"); const fs = require("fs"); const path = require("path");
const urls = [ 'https://example.com/file1.jpg', 'https://example.com/file2.jpg', 'https://example.com/file3.jpg' ];
const requestPromise = (url, filename) => { return new Promise((resolve, reject) => { request .get(url) .on("error", function(err) { console.error(`下载失败 ${url}:`, err.message); reject(err); }) .on("end", () => { console.log(`下载完成 ${filename}`); resolve(); }) .pipe(fs.createWriteStream(filename)); }); }
async function downloadSequentially() { for (let index = 0; index < urls.length; index++) { const url = urls[index]; const filename = path.basename(url); try { await requestPromise(url, filename); } catch (err) { console.error(`处理 ${url} 时出错:`, err.message); } } console.log('所有文件下载完成'); }
downloadSequentially();
|
方案二:使用 got 模块(推荐)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| const got = require('got'); const fs = require("fs"); const path = require("path");
const urls = [ 'https://example.com/file1.jpg', 'https://example.com/file2.jpg', 'https://example.com/file3.jpg' ];
async function downloadSequentially() { for (let index = 0; index < urls.length; index++) { const url = urls[index]; const filename = path.basename(url); try { await got.stream(url).pipe(fs.createWriteStream(filename)); console.log(`下载完成 ${filename}`); } catch (err) { console.error(`下载 ${url} 失败:`, err.message); } } console.log('所有文件下载完成'); }
downloadSequentially();
|
方案三:并发控制下载(推荐)
对于大量文件下载,顺序下载效率较低,但完全并发可能导致网络阻塞或服务器拒绝服务。更好的方式是控制并发数量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| const got = require('got'); const fs = require("fs"); const path = require("path");
const urls = [ 'https://example.com/file1.jpg', 'https://example.com/file2.jpg', 'https://example.com/file3.jpg', ];
async function downloadWithConcurrency(urls, concurrency = 3) { const results = []; for (let i = 0; i < urls.length; i += concurrency) { const batch = urls.slice(i, i + concurrency); const promises = batch.map(url => downloadSingleFile(url)); const batchResults = await Promise.allSettled(promises); results.push(...batchResults); } return results; }
async function downloadSingleFile(url) { const filename = path.basename(url); try { await got.stream(url).pipe(fs.createWriteStream(filename)); console.log(`✅ 下载完成 ${filename}`); return { url, filename, success: true }; } catch (err) { console.error(`❌ 下载 ${url} 失败:`, err.message); return { url, filename, success: false, error: err.message }; } }
(async () => { console.log(`开始下载 ${urls.length} 个文件。..`); const results = await downloadWithConcurrency(urls, 3); const successful = results.filter(r => r.status === 'fulfilled' && r.value.success).length; const failed = results.length - successful; console.log(`下载完成!成功:${successful}, 失败:${failed}`); })();
|
总结
- request 模块:虽然功能强大,但已被废弃,不建议在新项目中使用
- got 模块:现代化的 HTTP 客户端,支持 Promise 和流式操作,是目前的推荐选择
- 并发控制:在批量下载时非常重要,既能提高效率又能避免对服务器造成过大压力
根据实际需求选择合适的方案,对于小量文件可以选择顺序下载,对于大量文件建议使用并发控制方案。