// ZIP 打包处理模块 // 负责接收图片和 JSON 数据,打包成 ZIP 文件并返回 const archiver = require('archiver'); const http = require('http'); class ZipHandler { /** * 处理打包请求 * @param {http.IncomingMessage} req - HTTP 请求对象 * @param {http.ServerResponse} res - HTTP 响应对象 */ static handlePackRequest(req, res) { if (req.method !== 'POST') { res.writeHead(405, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Method not allowed' })); return; } let body = ''; req.on('data', (chunk) => { body += chunk.toString(); }); req.on('end', () => { try { const data = JSON.parse(body); const { folderName, imageData, jsonData } = data; if (!folderName || !imageData || !jsonData) { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Missing required fields: folderName, imageData, jsonData' })); return; } // 创建 ZIP 文件 this.createZip(folderName, imageData, jsonData, res); } catch (error) { console.error('[ZipHandler] 解析请求数据失败:', error); res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Invalid JSON data', details: error.message })); } }); req.on('error', (error) => { console.error('[ZipHandler] 请求错误:', error); res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Request error', details: error.message })); }); } /** * 创建 ZIP 文件并返回 * @param {string} folderName - 文件夹名称 * @param {string} imageData - Base64 编码的图片数据 * @param {string} jsonData - JSON 字符串数据 * @param {http.ServerResponse} res - HTTP 响应对象 */ static createZip(folderName, imageData, jsonData, res) { try { // 验证输入数据 if (!imageData || typeof imageData !== 'string') { throw new Error('imageData 必须是字符串'); } if (!jsonData || typeof jsonData !== 'string') { throw new Error('jsonData 必须是字符串'); } // 清理 Base64 数据:移除可能存在的 data URL 前缀 let cleanImageData = imageData; if (cleanImageData.includes(',')) { // 如果包含逗号,说明可能有 data:image/...;base64, 前缀 const base64Index = cleanImageData.indexOf(','); cleanImageData = cleanImageData.substring(base64Index + 1); } // 移除可能的空白字符 cleanImageData = cleanImageData.trim(); // 验证 Base64 格式 if (!/^[A-Za-z0-9+/=]+$/.test(cleanImageData)) { throw new Error('imageData 不是有效的 Base64 格式'); } // 设置响应头 res.writeHead(200, { 'Content-Type': 'application/zip', 'Content-Disposition': `attachment; filename="${encodeURIComponent(folderName)}.zip"`, 'Access-Control-Allow-Origin': '*' }); // 创建 archiver 实例 const archive = archiver('zip', { zlib: { level: 6 } // 压缩级别 }); // 监听错误 archive.on('error', (err) => { console.error('[ZipHandler] ZIP 创建错误:', err); if (!res.headersSent) { res.writeHead(500, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); res.end(JSON.stringify({ error: 'Failed to create zip', details: err.message })); } else { // 如果响应头已发送,尝试结束响应 try { res.end(); } catch (e) { console.error('[ZipHandler] 无法结束响应:', e); } } }); // 监听响应错误 res.on('error', (err) => { console.error('[ZipHandler] 响应错误:', err); archive.abort(); }); // 将 ZIP 流连接到响应 archive.pipe(res); // 将 Base64 图片数据转换为 Buffer let imageBuffer; try { imageBuffer = Buffer.from(cleanImageData, 'base64'); if (imageBuffer.length === 0) { throw new Error('Base64 解码后图片数据为空'); } } catch (bufferError) { throw new Error(`Base64 解码失败: ${bufferError.message}`); } // 添加文件到 ZIP archive.append(imageBuffer, { name: `${folderName}.png` }); archive.append(jsonData, { name: `${folderName}.json` }); // 完成打包 archive.finalize(); console.log(`[ZipHandler] ✓ 成功创建 ZIP: ${folderName}.zip`); } catch (error) { console.error('[ZipHandler] 创建 ZIP 失败:', error); if (!res.headersSent) { res.writeHead(500, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); res.end(JSON.stringify({ error: 'Failed to create zip', details: error.message })); } else { // 如果响应头已发送,尝试结束响应 try { res.end(); } catch (e) { console.error('[ZipHandler] 无法结束响应:', e); } } } } } module.exports = ZipHandler;