["http://xxxxx/xxxxx.jpg", ...]
node download.js
const fs = require('fs');
const path = require('path');
const https = require('https');
const http = require('http');
const CONFIG = {
maxConcurrent: 10,
timeout: 30000,
retryCount: 3,
outputDir: './image'
};
function loadUrls() {
try {
const data = fs.readFileSync('./finalresult_converted.json', 'utf8');
return JSON.parse(data);
} catch (error) {
console.error('读取URL文件失败:', error.message);
process.exit(1);
}
}
function getFileNameFromUrl(url) {
const urlPath = new URL(url).pathname;
return path.basename(urlPath);
}
function downloadFile(url, filePath, retries = CONFIG.retryCount) {
return new Promise((resolve, reject) => {
const protocol = url.startsWith('https:') ? https : http;
const request = protocol.get(url, {
timeout: CONFIG.timeout
}, (response) => {
if (response.statusCode !== 200) {
reject(new Error(`HTTP ${response.statusCode}: ${response.statusMessage}`));
return;
}
const fileStream = fs.createWriteStream(filePath);
response.pipe(fileStream);
fileStream.on('finish', () => {
fileStream.close();
resolve();
});
fileStream.on('error', (error) => {
fs.unlink(filePath, () => {});
reject(error);
});
});
request.on('error', (error) => {
reject(error);
});
request.on('timeout', () => {
request.destroy();
reject(new Error('请求超时'));
});
}).catch((error) => {
if (retries > 0) {
console.log(`下载失败,重试中... (剩余重试次数: ${retries}): ${url}`);
return new Promise(resolve => setTimeout(resolve, 1000))
.then(() => downloadFile(url, filePath, retries - 1));
}
throw error;
});
}
class DownloadManager {
constructor(maxConcurrent) {
this.maxConcurrent = maxConcurrent;
this.running = 0;
this.queue = [];
this.results = {
success: 0,
failed: 0,
errors: []
};
}
async add(url) {
return new Promise((resolve) => {
this.queue.push({ url, resolve });
this.process();
});
}
async process() {
if (this.running >= this.maxConcurrent || this.queue.length === 0) {
return;
}
const { url, resolve } = this.queue.shift();
this.running++;
try {
const fileName = getFileNameFromUrl(url);
const filePath = path.join(CONFIG.outputDir, fileName);
if (fs.existsSync(filePath)) {
console.log(`文件已存在,跳过: ${fileName}`);
this.results.success++;
} else {
console.log(`开始下载: ${fileName}`);
await downloadFile(url, filePath);
console.log(`下载完成: ${fileName}`);
this.results.success++;
}
} catch (error) {
console.error(`下载失败: ${url} - ${error.message}`);
this.results.failed++;
this.results.errors.push({ url, error: error.message });
}
this.running--;
resolve();
this.process();
}
async waitForCompletion() {
while (this.running > 0 || this.queue.length > 0) {
await new Promise(resolve => setTimeout(resolve, 100));
}
}
}
async function main() {
console.log('开始批量下载图片...');
console.log(`配置: 最大并发数=${CONFIG.maxConcurrent}, 超时=${CONFIG.timeout}ms, 重试次数=${CONFIG.retryCount}`);
if (!fs.existsSync(CONFIG.outputDir)) {
fs.mkdirSync(CONFIG.outputDir, { recursive: true });
}
const urls = loadUrls();
console.log(`共找到 ${urls.length} 个图片URL`);
const downloadManager = new DownloadManager(CONFIG.maxConcurrent);
const startTime = Date.now();
const downloadPromises = urls.map(url => downloadManager.add(url));
await Promise.all(downloadPromises);
await downloadManager.waitForCompletion();
const endTime = Date.now();
const duration = (endTime - startTime) / 1000;
console.log('\n=== 下载完成 ===');
console.log(`总耗时: ${duration.toFixed(2)} 秒`);
console.log(`成功下载: ${downloadManager.results.success} 个文件`);
console.log(`下载失败: ${downloadManager.results.failed} 个文件`);
if (downloadManager.results.errors.length > 0) {
console.log('\n失败的下载:');
downloadManager.results.errors.forEach(({ url, error }) => {
console.log(`- ${url}: ${error}`);
});
}
console.log(`\n所有图片已保存到: ${path.resolve(CONFIG.outputDir)}`);
}
if (require.main === module) {
main().catch(error => {
console.error('程序执行出错:', error);
process.exit(1);
});
}