文章目录
一、创建 Axios 实例
可以创建一个 axiosConfig.ts
文件用于创建和更新相关实例:
js
// server/utils/axiosConfig.ts
const axios = require("axios");
const { HttpsProxyAgent } = require('https-proxy-agent');
// axios 实例
let axiosInstance = null;
// 代理表单(可以从数据库读取相关配置)
let proxyParams = {
state: true,
host: "127.0.0.1",
port: 10809,
protocol: "http",
username: "",
password: ""
};
// 创建 axios 实例
const createAxiosInstance = () => {
// 判断是否开启代理
const useProxy = proxyParams['state'] == true;
let axiosConfig = {
timeout: 5000
};
// 启用代理服务
if (useProxy && proxyParams) {
const agent = new HttpsProxyAgent({
host: proxyParams.host,
port: proxyParams.port,
protocol: proxyParams.protocol,
});
axiosConfig['httpsAgent'] = agent;
}
// 代理服务器 用户名、密码
if (useProxy && proxyParams['username'] && proxyParams['password']) {
axiosConfig['auth'] = {
username: proxyParams['username'],
password: proxyParams['password']
};
}
axiosInstance = axios.create(axiosConfig);
}
// 获取 Axios 实例
const getAxiosInstance = () => {
if (!axiosInstance) {
createAxiosInstance();
}
return axiosInstance;
}
// 更新 Axios 实例
const updateAxiosInstance = () => {
createAxiosInstance();
}
module.exports = {
getAxiosInstance,
updateAxiosInstance
};
代理相关配置可以以字符串的方式先存储在数据库中,再根据需要读取和修改以实现动态更新代理配置:
js
const { getSettingCache } = require("../db/SettingManager.ts");
const createAxiosInstance = () => {
// 获取配置信息
const settingCache = getSettingCache();
// 判断是否开启代理
const proxyParams = settingCache["proxy"] ? JSON.parse(settingCache["proxy"]) : null;
// {
// proxy: '{"state":true,"host":"127.0.0.1","port":10809,"protocol":"http","username":"","password":""}',
// }
}
二、图片并发下载
此处使用 p-limit
来控制并发数量:
yarn add p-limit
创建一个 downloadImages.ts
文件用来处理图片下载相关方法:
js
// server/utils/downloadImages.ts
const { getAxiosInstance } = require('../utils/axiosConfig.ts');
const path = require("path");
const fs = require("fs");
const downloadImagesByUrls = async (imageUrls, savePath) => {
// 为避免出现循环导入,此处使用动态导入
const pLimit = (await import('p-limit')).default;
// 提取 url 中的文件名 例:xxx.jpg
const filenames = imageUrls.map((url) => path.basename(url));
// 判断是否存在目标文件夹,不存在则创建
if (!fs.existsSync(savePath)) {
fs.mkdirSync(savePath, { recursive: true });
}
// 下载单个图片的函数
const downloadImage = async (url, filename) => {
// 拼接文件路径
const fullPath = path.join(savePath, filename);
try {
// 获取 axios 实例
const axiosInstance = await getAxiosInstance();
const response = await axiosInstance({
method: "get",
url: url,
responseType: "stream",
});
const writer = await fs.createWriteStream(fullPath);
// 用Promise包装stream完成事件
return new Promise((resolve, reject) => {
response.data.pipe(writer);
writer.on("finish", () => resolve({ filename, success: true }));
writer.on("error", (err) => reject({ filename, success: false, error: err.message }));
response.data.on("error", (err) => reject({ filename, success: false, error: err.message }));
});
} catch (error) {
return { filename, success: false, error: error.message };
}
};
// 并发控制
const limit = pLimit(5);
// 创建下载任务
const tasks = imageUrls.map((url, index) =>
limit(() => downloadImage(url, filenames[index]))
);
// 执行下载任务并等待所有任务完成
const results = await Promise.all(tasks);
return results;
};
module.exports = {
downloadImagesByUrls,
};
代码说明: 该方法可直接复制使用,需要传递两个参数,imageUrls
参数为需要下载的图片列表,是一个存储了图片下载链接的数组,例:['http://xxx','http://xxx']
,第二个参数是 savePath
,即文件保存路径,例:D:/Desktop
,函数会提取文件名称进行路径的拼接,如果路径不存在则会自动创建。