const fs = require("fs-extra"); const path = require("path"); const axios = require("axios"); const FormData = require("form-data"); // ================= 配置区域 ================= const CONFIG = { baseUrl: "https://color.jccytech.cn", // 你的服务器地址 loginUrl: "/napi/web/auth/sign-in", // 登录接口路径 uploadUrl: "/napi/jigstack/web/jigstack", // 上传接口路径 credentials: { username: "guoziyun", // 管理员账号 password: "PD9IV73OQoLXAiyyT6Uo", // 管理员密码 }, metadataFile: "./image_metadata.json", // 上一步生成的 JSON 文件 concurrency: 1, // 并发数,建议先设为 1 稳定上传 }; let sessionCookie = ""; /** * 1. 模拟登录获取 Session */ async function login() { console.log("正在尝试登录..."); try { const response = await axios.post( `${CONFIG.baseUrl}${CONFIG.loginUrl}`, CONFIG.credentials ); // 从 set-cookie 头部提取 session id const cookies = response.headers["set-cookie"]; if (!cookies) throw new Error("登录失败,未获取到 Cookie"); // 格式通常是 connect.sid=s%3A...; Path=/; HttpOnly sessionCookie = cookies.map((c) => c.split(";")[0]).join("; "); console.log("登录成功,Session 已获取"); } catch (err) { console.error("登录异常:", err.message); process.exit(1); } } /** * 2. 执行单张图片上传 */ async function uploadImage(item) { console.log(`正在上传: ${item.filename} (ID: ${item.id})`); const form = new FormData(); // 基础参数 (注意:tags 需要转回逗号分隔的字符串,因为后端执行了 .split(',')) form.append("tags", item.tags.join(",")); form.append("from", item.from); form.append("width", item.width); form.append("height", item.height); // 文件流 (对应后端 fileHash.raw) // 如果你的后端 check 了字段名,必须确保这里是 'raw' const fileStream = fs.createReadStream(item.localPath); form.append("raw", fileStream); try { const response = await axios.post( `${CONFIG.baseUrl}${CONFIG.uploadUrl}`, form, { headers: { ...form.getHeaders(), // 必须包含 multipart 的 boundary Cookie: sessionCookie, }, // 防止大文件上传超时 maxContentLength: Infinity, maxBodyLength: Infinity, timeout: 60000, } ); console.log(`✅ 上传成功: ${item.id}, Server ID: ${response.data._id}`); return true; } catch (err) { const errorMsg = err.response ? JSON.stringify(err.response.data) : err.message; console.error(`❌ 上传失败: ${item.id} | 原因: ${errorMsg}`); return false; } } /** * 3. 主程序逻辑 */ async function startUpload() { // 1. 登录 await login(); // 2. 读取元数据 const metadata = await fs.readJson(CONFIG.metadataFile); console.log(`共加载 ${metadata.length} 条待上传数据`); // 3. 循环上传 for (const item of metadata) { const success = await uploadImage(item); // 如果失败了,可以根据需要决定是否停止或继续 if (!success) { console.log("跳过当前失败项目,继续下一个..."); } // 稍微停顿一下,避免瞬时压力过大 await new Promise((resolve) => setTimeout(resolve, 500)); } console.log("🎉 所有上传任务处理完毕"); } module.exports = { startUpload }; if (require.main === module) { startUpload(); }