require('dotenv').config(); const http = require('http'); const url = require('url'); const path = require('path'); const fs = require('fs'); const {sendResponse, isSafePath, processImage} = require("./utils"); const PORT = process.env.PORT || 3000; const IMAGE_BASE_DIR = process.env.IMAGE_BASE_DIR || '/app/images'; const server = http.createServer(async (req, res) => { if (req.method !== 'GET') { return sendResponse(res, 405, '仅支持GET请求'); } try { const parsedUrl = url.parse(req.url, true); const requestPath = parsedUrl.pathname; const query = parsedUrl.query; // 过滤根路径请求(避免直接访问服务根目录) if (requestPath === '/') { return sendResponse(res, 200, '图片处理服务已启动!'); } const requestedFilePath = requestPath.slice(1); const fullImagePath = path.join(IMAGE_BASE_DIR, requestedFilePath); // 安全检查:防止路径遍历 if (!isSafePath(fullImagePath, IMAGE_BASE_DIR)) { return sendResponse(res, 403, '访问拒绝:非法路径(路径遍历攻击)'); } if (!fs.existsSync(fullImagePath)) { return sendResponse(res, 404, `图片不存在:${requestedFilePath}`); } // 如果没有压缩参数,直接返回原图 if (!query.q && !query.w && !query.width && !query.h && !query.height) { const fileStream = fs.createReadStream(fullImagePath); const ext = path.extname(fullImagePath).slice(1); res.writeHead(200, {'Content-Type': `image/${ext}`}); // 流式传输文件(适合大文件) fileStream.pipe(res); fileStream.on('error', (err) => { sendResponse(res, 500, `读取文件失败:${err.message}`); }); return; } // 处理图片压缩和调整大小 const imageBuffer = await processImage(fullImagePath, query); res.writeHead(200, { 'Content-Type': 'image/jpeg', 'Content-Length': imageBuffer.length, 'Cache-Control': 'public, max-age=86400' // 缓存1天 }); res.end(imageBuffer); } catch (error) { console.error('服务器异常:', error); sendResponse(res, 500, `服务器内部错误:${error.message}`); } }); // ===================== 启动服务器 ===================== server.listen(PORT, '0.0.0.0', () => { console.log(`服务地址:http://0.0.0.0:${PORT}`); }); // 捕获全局未处理异常 process.on('uncaughtException', (err) => { console.error('全局未捕获异常:', err); }); process.on('unhandledRejection', (reason) => { console.error('全局未处理Promise拒绝:', reason); });