165 行
4.6 KiB
JavaScript
165 行
4.6 KiB
JavaScript
|
|
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 }
|