From 4a9d31806b44540611198aebe44f83c19a51dd02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=96=BB=E5=8B=87=E7=A5=A5?= <237899745@qq.com> Date: Tue, 11 Nov 2025 13:46:33 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=B8=8A=E4=BC=A0=E4=B8=B4=E6=97=B6=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=B8=85=E7=90=86=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题描述: - 文件删除操作可能抛出异常导致程序中断 - 服务器崩溃或重启后残留临时文件无法清理 - 没有自动清理机制 修复内容: 1. 添加safeDeleteFile()安全删除函数 - 使用try-catch捕获删除异常 - 记录删除日志和错误信息 - 不会因删除失败而中断主流程 2. 添加cleanupOldTempFiles()定期清理函数 - 启动时自动清理超过24小时的临时文件 - 防止临时文件堆积占用磁盘空间 - 容错处理避免清理失败影响启动 3. 替换所有fs.unlinkSync为safeDeleteFile - 文件大小超限时的临时文件清理 - 上传成功后的临时文件清理 - 上传失败时的临时文件清理 影响范围: 文件上传功能 测试建议: - 上传文件后检查uploads目录临时文件已删除 - 重启服务器验证旧临时文件自动清理 - 模拟删除失败场景验证不影响主流程 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- backend/server.js | 57 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/backend/server.js b/backend/server.js index 4696767..feeb459 100644 --- a/backend/server.js +++ b/backend/server.js @@ -69,6 +69,54 @@ const shareFileCache = new Map(); // ===== 工具函数 ===== + +// 安全删除文件(不抛出异常) +function safeDeleteFile(filePath) { + try { + if (fs.existsSync(filePath)) { + fs.unlinkSync(filePath); + console.log(`[清理] 已删除临时文件: ${filePath}`); + return true; + } + } catch (error) { + console.error(`[清理] 删除临时文件失败: ${filePath}`, error.message); + return false; + } +} + +// 清理旧的临时文件(启动时执行一次) +function cleanupOldTempFiles() { + const uploadsDir = path.join(__dirname, 'uploads'); + if (!fs.existsSync(uploadsDir)) { + return; + } + + try { + const files = fs.readdirSync(uploadsDir); + const now = Date.now(); + const maxAge = 24 * 60 * 60 * 1000; // 24小时 + + let cleaned = 0; + files.forEach(file => { + const filePath = path.join(uploadsDir, file); + try { + const stats = fs.statSync(filePath); + if (now - stats.mtimeMs > maxAge) { + fs.unlinkSync(filePath); + cleaned++; + } + } catch (err) { + console.error(`[清理] 检查文件失败: ${filePath}`, err.message); + } + }); + + if (cleaned > 0) { + console.log(`[清理] 已清理 ${cleaned} 个超过24小时的临时文件`); + } + } catch (error) { + console.error('[清理] 清理临时文件目录失败:', error.message); + } +} // SFTP连接 async function connectToSFTP(config) { const sftp = new SftpClient(); @@ -696,7 +744,7 @@ app.post('/api/upload', authMiddleware, upload.single('file'), async (req, res) if (req.file.size > maxUploadSize) { // 删除已上传的临时文件 if (fs.existsSync(req.file.path)) { - fs.unlinkSync(req.file.path); + safeDeleteFile(req.file.path); } return res.status(413).json({ @@ -725,7 +773,7 @@ app.post('/api/upload', authMiddleware, upload.single('file'), async (req, res) console.log(`[上传] 文件上传成功: ${remoteFilePath}`); // 删除本地临时文件 - fs.unlinkSync(req.file.path); + safeDeleteFile(req.file.path); res.json({ success: true, @@ -738,7 +786,7 @@ app.post('/api/upload', authMiddleware, upload.single('file'), async (req, res) // 删除临时文件 if (fs.existsSync(req.file.path)) { - fs.unlinkSync(req.file.path); + safeDeleteFile(req.file.path); } res.status(500).json({ @@ -2154,6 +2202,9 @@ app.get("/s/:code", (req, res) => { res.redirect(frontendUrl); }); +// 启动时清理旧临时文件 +cleanupOldTempFiles(); + // 启动服务器 app.listen(PORT, '0.0.0.0', () => { console.log(`\n========================================`);