server.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. const express = require('express')
  2. const cors = require('cors')
  3. const multer = require('multer')
  4. const path = require('path')
  5. const fs = require('fs-extra')
  6. const { calculateFileMD5 } = require('./utils.js')
  7. const { initDatabase, FileService } = require('../database/database.js')
  8. const app = express()
  9. const PORT = 3000
  10. // 初始化数据库
  11. initDatabase()
  12. const fileService = new FileService()
  13. // 确保上传目录存在
  14. const uploadDir = path.join(process.cwd(), 'uploads')
  15. fs.ensureDirSync(uploadDir)
  16. // 配置 multer - 修复中文文件名问题
  17. const storage = multer.diskStorage({
  18. destination: (req, file, cb) => {
  19. cb(null, uploadDir)
  20. },
  21. filename: (req, file, cb) => {
  22. // 处理中文文件名 - 使用原始文件名但确保安全
  23. const originalName = Buffer.from(file.originalname, 'latin1').toString('utf8')
  24. const ext = path.extname(originalName)
  25. const name = path.basename(originalName, ext)
  26. // 清理文件名,移除特殊字符
  27. const safeName = name.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, '_')
  28. const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9)
  29. const filename = safeName + '-' + uniqueSuffix + ext
  30. cb(null, filename)
  31. }
  32. })
  33. const upload = multer({
  34. storage,
  35. fileFilter: (req, file, cb) => {
  36. // 处理文件名编码
  37. file.originalname = Buffer.from(file.originalname, 'latin1').toString('utf8')
  38. cb(null, true)
  39. }
  40. })
  41. // 设置响应头,确保使用 UTF-8 编码
  42. app.use((req, res, next) => {
  43. res.setHeader('Content-Type', 'application/json; charset=utf-8')
  44. next()
  45. })
  46. app.use(cors())
  47. app.use(express.json({ limit: '50mb' }))
  48. app.use(express.urlencoded({ extended: true, limit: '50mb' }))
  49. // 文件上传接口
  50. app.post('/api/upload', upload.single('file'), async (req, res) => {
  51. try {
  52. if (!req.file) {
  53. return res.status(400).json({ error: 'No file uploaded' })
  54. }
  55. // 确保文件名正确编码
  56. const originalName = Buffer.from(req.file.originalname, 'latin1').toString('utf8')
  57. const fileInfo = {
  58. originalName: originalName,
  59. fileName: req.file.filename,
  60. filePath: req.file.path,
  61. fileSize: req.file.size,
  62. mimeType: req.file.mimetype
  63. }
  64. // 计算 MD5
  65. const md5 = await calculateFileMD5(req.file.path)
  66. // 保存到数据库
  67. const fileRecord = await fileService.createFile({
  68. ...fileInfo,
  69. md5
  70. })
  71. res.json({
  72. success: true,
  73. data: fileRecord
  74. })
  75. } catch (error) {
  76. console.error('Upload error:', error)
  77. res.status(500).json({ error: 'Upload failed: ' + error.message })
  78. }
  79. })
  80. // 获取文件列表(分页)
  81. app.get('/api/files', async (req, res) => {
  82. try {
  83. const page = parseInt(req.query.page) || 1
  84. const pageSize = parseInt(req.query.pageSize) || 10
  85. const result = await fileService.getFilesPaginated(page, pageSize)
  86. // 确保返回的数据使用 UTF-8 编码
  87. res.json(result)
  88. } catch (error) {
  89. console.error('Get files error:', error)
  90. res.status(500).json({ error: 'Failed to get files' })
  91. }
  92. })
  93. // 其他接口保持不变...
  94. app.post('/api/files/:id/check-md5', async (req, res) => {
  95. try {
  96. const fileId = parseInt(req.params.id)
  97. const file = await fileService.getFileById(fileId)
  98. if (!file) {
  99. return res.status(404).json({ error: 'File not found' })
  100. }
  101. const currentMD5 = await calculateFileMD5(file.filePath)
  102. const isChanged = currentMD5 !== file.md5
  103. res.json({
  104. isChanged,
  105. currentMD5,
  106. originalMD5: file.md5,
  107. file
  108. })
  109. } catch (error) {
  110. console.error('MD5 check error:', error)
  111. res.status(500).json({ error: 'MD5 check failed' })
  112. }
  113. })
  114. app.put('/api/files/:id/update-md5', async (req, res) => {
  115. try {
  116. const fileId = parseInt(req.params.id)
  117. const { md5 } = req.body
  118. await fileService.updateFileMD5(fileId, md5)
  119. res.json({ success: true })
  120. } catch (error) {
  121. console.error('Update MD5 error:', error)
  122. res.status(500).json({ error: 'Update failed' })
  123. }
  124. })
  125. // 健康检查接口
  126. app.get('/api/health', (req, res) => {
  127. res.json({
  128. status: 'OK',
  129. timestamp: new Date().toISOString(),
  130. service: 'file-management-api'
  131. })
  132. })
  133. function startServer() {
  134. app.listen(PORT, () => {
  135. console.log(`Server running on http://localhost:${PORT}`)
  136. })
  137. }
  138. module.exports = { startServer }