const fs = require('fs');
const path = require('path');
const axios = require('axios');
/**
* 下载文件到本地
* @param {string} fileUrl - 文件的远程URL
* @param {string} targetDir - 本地目标文件夹(如 './downloads')
* @param {string} [fileName] - 自定义文件名(可选,默认从URL或响应头获取)
*/
async function downloadFile(fileUrl, targetDir, fileName) {
try {
// 1. 确保目标文件夹存在
if (!fs.existsSync(targetDir)) {
fs.mkdirSync(targetDir, { recursive: true });
}
// 2. 发送HTTP请求(流式响应)
const response = await axios({
method: 'get',
url: fileUrl,
responseType: 'stream', // 重要:以流的形式接收数据
});
// 3. 确定文件名(优先级:自定义名称 > URL中的文件名 > 响应头中的文件名)
let finalFileName = fileName;
if (!finalFileName) {
// 从URL中提取文件名(如 https://example.com/file.zip -> 'file.zip')
const urlFileName = path.basename(new URL(fileUrl).pathname);
if (urlFileName) finalFileName = urlFileName;
// 尝试从响应头获取文件名(如 Content-Disposition: attachment; filename="example.pdf")
const contentDisposition = response.headers['content-disposition'];
if (contentDisposition) {
const match = contentDisposition.match(/filename="?(.+?)"?(;|$)/i);
if (match?.[1]) finalFileName = match[1];
}
}
if (!finalFileName) {
throw new Error('无法确定文件名,请通过参数手动指定。');
}
// 4. 拼接本地保存路径
const filePath = path.join(targetDir, finalFileName);
// 5. 创建可写流并保存文件
const writer = fs.createWriteStream(filePath);
response.data.pipe(writer);
return new Promise((resolve, reject) => {
writer.on('finish', () => resolve(filePath));
writer.on('error', reject);
});
} catch (error) {
throw new Error(`下载失败: ${error.message}`);
}
}
/**
* 批量下载文件(支持并发控制)
* @param {Array} urls - 文件URL列表
* @param {string} targetDir - 保存目录
* @param {number} [concurrency=3] - 并发数
*/
async function batchDownload(urls, targetDir, concurrency = 3) {
const results = [];
const queue = [...urls];
async function processQueue() {
while (queue.length > 0) {
const url = queue.shift();
try {
const filePath = await downloadFile(url, targetDir);
results.push({ url, status: 'success', path: filePath });
console.log(`✅ 下载成功: ${url} -> ${filePath}`);
} catch (error) {
results.push({ url, status: 'failed', error: error.message });
console.error(`❌ 下载失败: ${url} - ${error.message}`);
}
}
}
// 启动并发任务
const workers = Array(concurrency).fill().map(processQueue);
await Promise.all(workers);
return results;
}
// 使用示例
(async () => {
const fileUrls = [
'https://example.com/file1.pdf',
'https://example.com/file2.zip',
'https://example.com/file3.jpg',
// 添加更多URL...
];
const targetDir = './name_downloads'; // 本地文件夹
const concurrency = 2; // 并发数(根据网络调整)
console.log('开始批量下载...');
const results = await batchDownload(fileUrls, targetDir, concurrency);
console.log('\n下载统计:');
console.table(results);
})();
测试运行
-
将代码保存为
download.js。 -
在终端执行:
bash
node download.js -
成功时输出类似:
text
文件已保存到: ./downloads/sample.pdf