const express = require('express') const cors = require('cors') const multer = require('multer') const path = require('path') const fs = require('fs-extra') const { calculateFileMD5 } = require('./utils.js') const { initDatabase, FileService } = require('../database/database.js') const app = express() const PORT = 3000 // 初始化数据库 initDatabase() const fileService = new FileService() // 确保上传目录存在 const uploadDir = path.join(process.cwd(), 'uploads') fs.ensureDirSync(uploadDir) // 配置 multer - 修复中文文件名问题 const storage = multer.diskStorage({ destination: (req, file, cb) => { cb(null, uploadDir) }, filename: (req, file, cb) => { // 处理中文文件名 - 使用原始文件名但确保安全 const originalName = Buffer.from(file.originalname, 'latin1').toString('utf8') const ext = path.extname(originalName) const name = path.basename(originalName, ext) // 清理文件名,移除特殊字符 const safeName = name.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, '_') const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9) const filename = safeName + '-' + uniqueSuffix + ext cb(null, filename) } }) const upload = multer({ storage, fileFilter: (req, file, cb) => { // 处理文件名编码 file.originalname = Buffer.from(file.originalname, 'latin1').toString('utf8') cb(null, true) } }) // 设置响应头,确保使用 UTF-8 编码 app.use((req, res, next) => { res.setHeader('Content-Type', 'application/json; charset=utf-8') next() }) app.use(cors()) app.use(express.json({ limit: '50mb' })) app.use(express.urlencoded({ extended: true, limit: '50mb' })) // 文件上传接口 app.post('/api/upload', upload.single('file'), async (req, res) => { try { if (!req.file) { return res.status(400).json({ error: 'No file uploaded' }) } // 确保文件名正确编码 const originalName = Buffer.from(req.file.originalname, 'latin1').toString('utf8') const fileInfo = { originalName: originalName, fileName: req.file.filename, filePath: req.file.path, fileSize: req.file.size, mimeType: req.file.mimetype } // 计算 MD5 const md5 = await calculateFileMD5(req.file.path) // 保存到数据库 const fileRecord = await fileService.createFile({ ...fileInfo, md5 }) res.json({ success: true, data: fileRecord }) } catch (error) { console.error('Upload error:', error) res.status(500).json({ error: 'Upload failed: ' + error.message }) } }) // 获取文件列表(分页) app.get('/api/files', async (req, res) => { try { const page = parseInt(req.query.page) || 1 const pageSize = parseInt(req.query.pageSize) || 10 const result = await fileService.getFilesPaginated(page, pageSize) // 确保返回的数据使用 UTF-8 编码 res.json(result) } catch (error) { console.error('Get files error:', error) res.status(500).json({ error: 'Failed to get files' }) } }) // 其他接口保持不变... app.post('/api/files/:id/check-md5', async (req, res) => { try { const fileId = parseInt(req.params.id) const file = await fileService.getFileById(fileId) if (!file) { return res.status(404).json({ error: 'File not found' }) } const currentMD5 = await calculateFileMD5(file.filePath) const isChanged = currentMD5 !== file.md5 res.json({ isChanged, currentMD5, originalMD5: file.md5, file }) } catch (error) { console.error('MD5 check error:', error) res.status(500).json({ error: 'MD5 check failed' }) } }) app.put('/api/files/:id/update-md5', async (req, res) => { try { const fileId = parseInt(req.params.id) const { md5 } = req.body await fileService.updateFileMD5(fileId, md5) res.json({ success: true }) } catch (error) { console.error('Update MD5 error:', error) res.status(500).json({ error: 'Update failed' }) } }) // 健康检查接口 app.get('/api/health', (req, res) => { res.json({ status: 'OK', timestamp: new Date().toISOString(), service: 'file-management-api' }) }) function startServer() { app.listen(PORT, () => { console.log(`Server running on http://localhost:${PORT}`) }) } module.exports = { startServer }