diff --git a/backend/server.js b/backend/server.js index 9954903..f90e308 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1066,6 +1066,73 @@ app.post('/api/files/rename', authMiddleware, async (req, res) => { } }); +// 创建文件夹 +app.post('/api/files/mkdir', authMiddleware, async (req, res) => { + const { path, folderName } = req.body; + let storage; + + // 参数验证 + if (!folderName || folderName.trim() === '') { + return res.status(400).json({ + success: false, + message: '文件夹名称不能为空' + }); + } + + // 文件名安全检查 - 防止路径遍历攻击 + if (folderName.includes('/') || folderName.includes('\\') || folderName.includes('..') || folderName.includes(':')) { + return res.status(400).json({ + success: false, + message: '文件夹名称不能包含特殊字符 (/ \\ .. :)' + }); + } + + // 只允许本地存储创建文件夹 + if (req.user.current_storage_type !== 'local') { + return res.status(400).json({ + success: false, + message: '只有本地存储支持创建文件夹' + }); + } + + try { + const { StorageInterface } = require('./storage'); + const storageInterface = new StorageInterface(req.user); + storage = await storageInterface.connect(); + + // 构造文件夹路径 + const basePath = path || '/'; + const folderPath = basePath === '/' ? `/${folderName}` : `${basePath}/${folderName}`; + const fullPath = storage.getFullPath(folderPath); + + // 检查是否已存在 + if (fs.existsSync(fullPath)) { + return res.status(400).json({ + success: false, + message: '文件夹已存在' + }); + } + + // 创建文件夹 (不使用recursive,只创建当前层级) + fs.mkdirSync(fullPath, { mode: 0o755 }); + + console.log(`[创建文件夹成功] 用户${req.user.id}: ${folderPath}`); + + res.json({ + success: true, + message: '文件夹创建成功' + }); + } catch (error) { + console.error('[创建文件夹失败]', error); + res.status(500).json({ + success: false, + message: '创建文件夹失败: ' + error.message + }); + } finally { + if (storage) await storage.end(); + } +}); + // 删除文件 app.post('/api/files/delete', authMiddleware, async (req, res) => { const { fileName, path } = req.body; @@ -1090,7 +1157,7 @@ app.post('/api/files/delete', authMiddleware, async (req, res) => { res.json({ success: true, - message: '文件删除成功' + message: '删除成功' }); } catch (error) { console.error('删除文件失败:', error); diff --git a/backend/storage.js b/backend/storage.js index 4257508..493ef6f 100644 --- a/backend/storage.js +++ b/backend/storage.js @@ -137,16 +137,55 @@ class LocalStorageClient { } /** - * 删除文件 + * 删除文件或文件夹 */ async delete(filePath) { const fullPath = this.getFullPath(filePath); const stats = fs.statSync(fullPath); - fs.unlinkSync(fullPath); + if (stats.isDirectory()) { + // 删除文件夹 - 递归删除 + // 先计算文件夹内所有文件的总大小 + const folderSize = this.calculateFolderSize(fullPath); - // 更新已使用空间 - this.updateUsedSpace(-stats.size); + // 删除文件夹及其内容 + fs.rmSync(fullPath, { recursive: true, force: true }); + + // 更新已使用空间 + if (folderSize > 0) { + this.updateUsedSpace(-folderSize); + } + } else { + // 删除文件 + fs.unlinkSync(fullPath); + + // 更新已使用空间 + this.updateUsedSpace(-stats.size); + } + } + + /** + * 计算文件夹大小 + */ + calculateFolderSize(folderPath) { + let totalSize = 0; + + const items = fs.readdirSync(folderPath, { withFileTypes: true }); + + for (const item of items) { + const itemPath = path.join(folderPath, item.name); + + if (item.isDirectory()) { + // 递归计算子文件夹 + totalSize += this.calculateFolderSize(itemPath); + } else { + // 累加文件大小 + const stats = fs.statSync(itemPath); + totalSize += stats.size; + } + } + + return totalSize; } /** diff --git a/frontend/app.html b/frontend/app.html index 6de58f6..21cb2f9 100644 --- a/frontend/app.html +++ b/frontend/app.html @@ -669,6 +669,9 @@ + + + + + +