zip.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. // ZIP 打包处理模块
  2. // 负责接收图片和 JSON 数据,打包成 ZIP 文件并返回
  3. const archiver = require('archiver');
  4. const http = require('http');
  5. class ZipHandler {
  6. /**
  7. * 处理打包请求
  8. * @param {http.IncomingMessage} req - HTTP 请求对象
  9. * @param {http.ServerResponse} res - HTTP 响应对象
  10. */
  11. static handlePackRequest(req, res) {
  12. if (req.method !== 'POST') {
  13. res.writeHead(405, { 'Content-Type': 'application/json' });
  14. res.end(JSON.stringify({ error: 'Method not allowed' }));
  15. return;
  16. }
  17. let body = '';
  18. req.on('data', (chunk) => {
  19. body += chunk.toString();
  20. });
  21. req.on('end', () => {
  22. try {
  23. const data = JSON.parse(body);
  24. const { folderName, imageData, jsonData } = data;
  25. if (!folderName || !imageData || !jsonData) {
  26. res.writeHead(400, { 'Content-Type': 'application/json' });
  27. res.end(JSON.stringify({ error: 'Missing required fields: folderName, imageData, jsonData' }));
  28. return;
  29. }
  30. // 创建 ZIP 文件
  31. this.createZip(folderName, imageData, jsonData, res);
  32. } catch (error) {
  33. console.error('[ZipHandler] 解析请求数据失败:', error);
  34. res.writeHead(400, { 'Content-Type': 'application/json' });
  35. res.end(JSON.stringify({ error: 'Invalid JSON data', details: error.message }));
  36. }
  37. });
  38. req.on('error', (error) => {
  39. console.error('[ZipHandler] 请求错误:', error);
  40. res.writeHead(500, { 'Content-Type': 'application/json' });
  41. res.end(JSON.stringify({ error: 'Request error', details: error.message }));
  42. });
  43. }
  44. /**
  45. * 创建 ZIP 文件并返回
  46. * @param {string} folderName - 文件夹名称
  47. * @param {string} imageData - Base64 编码的图片数据
  48. * @param {string} jsonData - JSON 字符串数据
  49. * @param {http.ServerResponse} res - HTTP 响应对象
  50. */
  51. static createZip(folderName, imageData, jsonData, res) {
  52. try {
  53. // 验证输入数据
  54. if (!imageData || typeof imageData !== 'string') {
  55. throw new Error('imageData 必须是字符串');
  56. }
  57. if (!jsonData || typeof jsonData !== 'string') {
  58. throw new Error('jsonData 必须是字符串');
  59. }
  60. // 清理 Base64 数据:移除可能存在的 data URL 前缀
  61. let cleanImageData = imageData;
  62. if (cleanImageData.includes(',')) {
  63. // 如果包含逗号,说明可能有 data:image/...;base64, 前缀
  64. const base64Index = cleanImageData.indexOf(',');
  65. cleanImageData = cleanImageData.substring(base64Index + 1);
  66. }
  67. // 移除可能的空白字符
  68. cleanImageData = cleanImageData.trim();
  69. // 验证 Base64 格式
  70. if (!/^[A-Za-z0-9+/=]+$/.test(cleanImageData)) {
  71. throw new Error('imageData 不是有效的 Base64 格式');
  72. }
  73. // 设置响应头
  74. res.writeHead(200, {
  75. 'Content-Type': 'application/zip',
  76. 'Content-Disposition': `attachment; filename="${encodeURIComponent(folderName)}.zip"`,
  77. 'Access-Control-Allow-Origin': '*'
  78. });
  79. // 创建 archiver 实例
  80. const archive = archiver('zip', {
  81. zlib: { level: 6 } // 压缩级别
  82. });
  83. // 监听错误
  84. archive.on('error', (err) => {
  85. console.error('[ZipHandler] ZIP 创建错误:', err);
  86. if (!res.headersSent) {
  87. res.writeHead(500, {
  88. 'Content-Type': 'application/json',
  89. 'Access-Control-Allow-Origin': '*'
  90. });
  91. res.end(JSON.stringify({ error: 'Failed to create zip', details: err.message }));
  92. } else {
  93. // 如果响应头已发送,尝试结束响应
  94. try {
  95. res.end();
  96. } catch (e) {
  97. console.error('[ZipHandler] 无法结束响应:', e);
  98. }
  99. }
  100. });
  101. // 监听响应错误
  102. res.on('error', (err) => {
  103. console.error('[ZipHandler] 响应错误:', err);
  104. archive.abort();
  105. });
  106. // 将 ZIP 流连接到响应
  107. archive.pipe(res);
  108. // 将 Base64 图片数据转换为 Buffer
  109. let imageBuffer;
  110. try {
  111. imageBuffer = Buffer.from(cleanImageData, 'base64');
  112. if (imageBuffer.length === 0) {
  113. throw new Error('Base64 解码后图片数据为空');
  114. }
  115. } catch (bufferError) {
  116. throw new Error(`Base64 解码失败: ${bufferError.message}`);
  117. }
  118. // 添加文件到 ZIP
  119. archive.append(imageBuffer, { name: `${folderName}.png` });
  120. archive.append(jsonData, { name: `${folderName}.json` });
  121. // 完成打包
  122. archive.finalize();
  123. console.log(`[ZipHandler] ✓ 成功创建 ZIP: ${folderName}.zip`);
  124. } catch (error) {
  125. console.error('[ZipHandler] 创建 ZIP 失败:', error);
  126. if (!res.headersSent) {
  127. res.writeHead(500, {
  128. 'Content-Type': 'application/json',
  129. 'Access-Control-Allow-Origin': '*'
  130. });
  131. res.end(JSON.stringify({ error: 'Failed to create zip', details: error.message }));
  132. } else {
  133. // 如果响应头已发送,尝试结束响应
  134. try {
  135. res.end();
  136. } catch (e) {
  137. console.error('[ZipHandler] 无法结束响应:', e);
  138. }
  139. }
  140. }
  141. }
  142. }
  143. module.exports = ZipHandler;