// PNG 序列动画预览工具 - 服务器主文件 // 负责启动 HTTP 服务器和静态文件服务 const http = require('http'); const fs = require('fs'); const path = require('path'); const url = require('url'); const TextureReader = require('./texture-reader'); const ZipHandler = require('./zip'); const DiskManager = require('./disk'); const ReplaceCharacterHandler = require('./replace-character'); const RemoveBackgroundBase64 = require('./remove-background-base64'); const PORT = 3000; const SERVER_DIR = __dirname; // Server 目录 const CLIENT_DIR = path.join(__dirname, '..', 'Client'); // Client 目录 // MIME 类型映射 const mimeTypes = { '.html': 'text/html', '.js': 'text/javascript', '.css': 'text/css', '.png': 'image/png', '.jpg': 'image/jpeg', '.gif': 'image/gif', '.json': 'application/json', }; // 初始化 TextureReader const textureReader = new TextureReader(SERVER_DIR); // 初始化 DiskManager const diskManager = new DiskManager(); // 创建 HTTP 服务器 const server = http.createServer((req, res) => { const parsedUrl = url.parse(req.url, true); let pathname = parsedUrl.pathname; // API: 获取可用的文件夹列表 if (pathname === '/api/folders') { textureReader.handleGetFolders(res); return; } // API: 获取指定文件夹中的帧文件列表 if (pathname.startsWith('/api/frames/')) { const encodedPath = pathname.replace('/api/frames/', ''); // 对路径的每一段进行解码 const folderName = encodedPath.split('/').map(seg => decodeURIComponent(seg)).join('/'); console.log('[Server] API请求帧列表, 原始:', encodedPath, '解码后:', folderName); textureReader.handleGetFrames(folderName, res); return; } // API: 打包 sprite sheet 为 ZIP if (pathname === '/api/pack') { ZipHandler.handlePackRequest(req, res); return; } // API: 角色替换(图生图) if (pathname === '/api/replace-character') { ReplaceCharacterHandler.handleReplaceRequest(req, res); return; } // API: Base64 图片抠图 if (pathname === '/api/remove-background-base64') { RemoveBackgroundBase64.handleRequest(req, res); return; } // API: 网盘 - 获取文件列表 if (pathname === '/api/disk/list') { diskManager.handleListRequest(req, res); return; } // API: 网盘 - 上传文件 if (pathname === '/api/disk/upload') { diskManager.handleUploadRequest(req, res); return; } // API: 网盘 - 创建文件夹 if (pathname === '/api/disk/create-folder') { diskManager.handleCreateFolderRequest(req, res); return; } // API: 网盘 - 下载文件 if (pathname === '/api/disk/download') { diskManager.handleDownloadRequest(req, res); return; } // API: 网盘 - 重命名 if (pathname === '/api/disk/rename') { diskManager.handleRenameRequest(req, res); return; } // API: 网盘 - 图片预览 if (pathname === '/api/disk/preview') { diskManager.handlePreviewRequest(req, res); return; } // API: 网盘 - 移动文件/文件夹 if (pathname === '/api/disk/move') { diskManager.handleMoveRequest(req, res); return; } // API: 网盘 - 复制文件/文件夹 if (pathname === '/api/disk/copy') { diskManager.handleCopyRequest(req, res); return; } // API: 网盘 - 删除文件/文件夹 if (pathname === '/api/disk/delete') { diskManager.handleDeleteRequest(req, res); return; } // API: 网盘 - 一键抠背景 if (pathname === '/api/disk/remove-background') { diskManager.handleRemoveBackgroundRequest(req, res); return; } // API: 网盘 - 剪裁最小区域 if (pathname === '/api/disk/crop-mini') { diskManager.handleCropMiniRequest(req, res); return; } // 默认首页 if (pathname === '/') { pathname = '/index.html'; } let filePath; // 如果是 texture 或 disk_data 相关的请求,从 server 目录提供 if (pathname.startsWith('/texture/') || pathname.startsWith('/disk_data/')) { // 移除开头的 /,然后拼接路径 const relativePath = pathname.substring(1); // 移除开头的 / // 解码 URL 编码的路径(处理中文、空格等特殊字符) const decodedPath = decodeURIComponent(relativePath); filePath = path.join(SERVER_DIR, decodedPath); } else { // 其他请求从 Client 目录提供 const relativePath = pathname.startsWith('/') ? pathname.substring(1) : pathname; // 解码 URL 编码的路径 const decodedPath = decodeURIComponent(relativePath); filePath = path.join(CLIENT_DIR, decodedPath); } // 检查文件是否存在并获取文件信息 fs.stat(filePath, (statErr, stats) => { if (statErr) { // 文件不存在,返回 404(静默处理) res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end(''); return; } // 获取文件扩展名 const ext = path.extname(filePath).toLowerCase(); const contentType = mimeTypes[ext] || 'application/octet-stream'; // 构建响应头 const headers = { 'Content-Type': contentType }; // 如果是图片文件,添加缓存头 if (ext === '.png' || ext === '.jpg' || ext === '.jpeg' || ext === '.gif') { // 设置长期缓存(1年) headers['Cache-Control'] = 'public, max-age=31536000, immutable'; // 添加 ETag 用于缓存验证 const etag = `"${stats.mtime.getTime()}-${stats.size}"`; headers['ETag'] = etag; // 检查客户端是否发送了 If-None-Match 头(缓存验证) const ifNoneMatch = req.headers['if-none-match']; if (ifNoneMatch === etag) { // 文件未修改,返回 304 Not Modified res.writeHead(304, headers); res.end(); return; } } // 读取文件 fs.readFile(filePath, (readErr, data) => { if (readErr) { res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Internal Server Error'); return; } // 返回文件 res.writeHead(200, headers); res.end(data); }); }); }); // 启动服务器 server.listen(PORT, () => { console.log('========================================'); console.log(' PNG 序列动画预览工具 - 服务器'); console.log('========================================'); console.log(`服务器运行在: http://localhost:${PORT}`); console.log(`图片资源路径: ${path.join(SERVER_DIR, 'disk_data')}`); console.log('========================================'); console.log('按 Ctrl+C 或关闭此窗口停止服务器'); console.log(''); }); // 优雅关闭处理 function gracefulShutdown() { console.log('\n正在关闭服务器...'); server.close(() => { console.log('服务器已关闭'); process.exit(0); }); } process.on('SIGINT', gracefulShutdown); process.on('SIGTERM', gracefulShutdown); // Windows 下处理窗口关闭事件 if (process.platform === 'win32') { const readline = require('readline'); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); rl.on('SIGINT', () => { process.emit('SIGINT'); }); }