Nodejs 批量下载文件

之前就已经写过一篇 浅谈 js 异步,最近有人问我怎么写批量下载。

在日常开发中,我们经常会遇到需要批量下载文件的场景,比如下载图片集合、备份资源文件等。本文将介绍几种在 Node.js 中实现批量下载文件的方法。

方案概览

实现批量下载有多种方案:

  1. 使用 request 模块自己封装 Promise 实现批量下载
  2. 使用封装好的 got 模块直接批量下载
  3. 使用 axios 等现代 HTTP 客户端库
  4. 实现并发控制以提高下载效率

需要注意的是,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");

// 示例 URL 数组
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");

// 示例 URL 数组
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");

// 示例 URL 数组
const urls = [
'https://example.com/file1.jpg',
'https://example.com/file2.jpg',
'https://example.com/file3.jpg',
// ... 更多 URLs
];

// 控制并发数的下载函数
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); // 最多同时下载 3 个文件

const successful = results.filter(r => r.status === 'fulfilled' && r.value.success).length;
const failed = results.length - successful;

console.log(`下载完成!成功:${successful}, 失败:${failed}`);
})();

总结

  1. request 模块:虽然功能强大,但已被废弃,不建议在新项目中使用
  2. got 模块:现代化的 HTTP 客户端,支持 Promise 和流式操作,是目前的推荐选择
  3. 并发控制:在批量下载时非常重要,既能提高效率又能避免对服务器造成过大压力

根据实际需求选择合适的方案,对于小量文件可以选择顺序下载,对于大量文件建议使用并发控制方案。


Nodejs 批量下载文件
https://bubao.github.io/posts/6b6ddd9c.html
作者
一念
发布于
2021年5月22日
许可协议