uploader.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. const fs = require("fs-extra");
  2. const path = require("path");
  3. const axios = require("axios");
  4. const FormData = require("form-data");
  5. // ================= 配置区域 =================
  6. const CONFIG = {
  7. baseUrl: "https://color.jccytech.cn", // 你的服务器地址
  8. loginUrl: "/napi/web/auth/sign-in", // 登录接口路径
  9. uploadUrl: "/napi/jigstack/web/jigstack", // 上传接口路径
  10. credentials: {
  11. username: "guoziyun", // 管理员账号
  12. password: "PD9IV73OQoLXAiyyT6Uo", // 管理员密码
  13. },
  14. metadataFile: "./image_metadata.json", // 上一步生成的 JSON 文件
  15. concurrency: 1, // 并发数,建议先设为 1 稳定上传
  16. };
  17. let sessionCookie = "";
  18. /**
  19. * 1. 模拟登录获取 Session
  20. */
  21. async function login() {
  22. console.log("正在尝试登录...");
  23. try {
  24. const response = await axios.post(
  25. `${CONFIG.baseUrl}${CONFIG.loginUrl}`,
  26. CONFIG.credentials
  27. );
  28. // 从 set-cookie 头部提取 session id
  29. const cookies = response.headers["set-cookie"];
  30. if (!cookies) throw new Error("登录失败,未获取到 Cookie");
  31. // 格式通常是 connect.sid=s%3A...; Path=/; HttpOnly
  32. sessionCookie = cookies.map((c) => c.split(";")[0]).join("; ");
  33. console.log("登录成功,Session 已获取");
  34. } catch (err) {
  35. console.error("登录异常:", err.message);
  36. process.exit(1);
  37. }
  38. }
  39. /**
  40. * 2. 执行单张图片上传
  41. */
  42. async function uploadImage(item) {
  43. console.log(`正在上传: ${item.filename} (ID: ${item.id})`);
  44. const form = new FormData();
  45. // 基础参数 (注意:tags 需要转回逗号分隔的字符串,因为后端执行了 .split(','))
  46. form.append("tags", item.tags.join(","));
  47. form.append("from", item.from);
  48. form.append("width", item.width);
  49. form.append("height", item.height);
  50. // 文件流 (对应后端 fileHash.raw)
  51. // 如果你的后端 check 了字段名,必须确保这里是 'raw'
  52. const fileStream = fs.createReadStream(item.localPath);
  53. form.append("raw", fileStream);
  54. try {
  55. const response = await axios.post(
  56. `${CONFIG.baseUrl}${CONFIG.uploadUrl}`,
  57. form,
  58. {
  59. headers: {
  60. ...form.getHeaders(), // 必须包含 multipart 的 boundary
  61. Cookie: sessionCookie,
  62. },
  63. // 防止大文件上传超时
  64. maxContentLength: Infinity,
  65. maxBodyLength: Infinity,
  66. timeout: 60000,
  67. }
  68. );
  69. console.log(`✅ 上传成功: ${item.id}, Server ID: ${response.data._id}`);
  70. return true;
  71. } catch (err) {
  72. const errorMsg = err.response
  73. ? JSON.stringify(err.response.data)
  74. : err.message;
  75. console.error(`❌ 上传失败: ${item.id} | 原因: ${errorMsg}`);
  76. return false;
  77. }
  78. }
  79. /**
  80. * 3. 主程序逻辑
  81. */
  82. async function startUpload() {
  83. // 1. 登录
  84. await login();
  85. // 2. 读取元数据
  86. const metadata = await fs.readJson(CONFIG.metadataFile);
  87. console.log(`共加载 ${metadata.length} 条待上传数据`);
  88. // 3. 循环上传
  89. for (const item of metadata) {
  90. const success = await uploadImage(item);
  91. // 如果失败了,可以根据需要决定是否停止或继续
  92. if (!success) {
  93. console.log("跳过当前失败项目,继续下一个...");
  94. }
  95. // 稍微停顿一下,避免瞬时压力过大
  96. await new Promise((resolve) => setTimeout(resolve, 500));
  97. }
  98. console.log("🎉 所有上传任务处理完毕");
  99. }
  100. module.exports = { startUpload };
  101. if (require.main === module) {
  102. startUpload();
  103. }