152 lines
4.0 KiB
JavaScript
152 lines
4.0 KiB
JavaScript
|
|
const express = require('express');
|
|||
|
|
const multer = require('multer');
|
|||
|
|
const path = require('path');
|
|||
|
|
const fs = require('fs');
|
|||
|
|
const { v4: uuidv4 } = require('uuid');
|
|||
|
|
const cors = require('cors');
|
|||
|
|
|
|||
|
|
const app = express();
|
|||
|
|
const PORT = 30005;
|
|||
|
|
const BASE_URL = 'http://1.15.149.240';
|
|||
|
|
|
|||
|
|
// 启用 CORS
|
|||
|
|
app.use(cors());
|
|||
|
|
app.use(express.json());
|
|||
|
|
|
|||
|
|
// 配置存储
|
|||
|
|
const storage = multer.diskStorage({
|
|||
|
|
destination: function (req, file, cb) {
|
|||
|
|
const model = req.body.model || 'default';
|
|||
|
|
const date = new Date();
|
|||
|
|
const year = date.getFullYear();
|
|||
|
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|||
|
|
const day = String(date.getDate()).padStart(2, '0');
|
|||
|
|
|
|||
|
|
// 根据 model 确定存储路径
|
|||
|
|
let modelPath = model;
|
|||
|
|
if (model === 'works' && file.mimetype.startsWith('video/')) {
|
|||
|
|
modelPath = 'video';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const uploadDir = path.join('/data/uploads', modelPath, year, month, day);
|
|||
|
|
|
|||
|
|
// 确保目录存在
|
|||
|
|
try {
|
|||
|
|
fs.mkdirSync(uploadDir, { recursive: true });
|
|||
|
|
console.log(`创建目录: ${uploadDir}`);
|
|||
|
|
} catch (err) {
|
|||
|
|
console.error(`创建目录失败: ${err.message}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
cb(null, uploadDir);
|
|||
|
|
},
|
|||
|
|
filename: function (req, file, cb) {
|
|||
|
|
const ext = path.extname(file.originalname);
|
|||
|
|
const filename = `${uuidv4()}${ext}`;
|
|||
|
|
cb(null, filename);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 文件过滤器
|
|||
|
|
const fileFilter = (req, file, cb) => {
|
|||
|
|
const allowedImageTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/bmp', 'image/webp'];
|
|||
|
|
const allowedVideoTypes = ['video/mp4', 'video/quicktime', 'video/x-msvideo', 'video/x-flv'];
|
|||
|
|
|
|||
|
|
if (allowedImageTypes.includes(file.mimetype) || allowedVideoTypes.includes(file.mimetype)) {
|
|||
|
|
cb(null, true);
|
|||
|
|
} else {
|
|||
|
|
cb(new Error('不支持的文件类型'), false);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const upload = multer({
|
|||
|
|
storage: storage,
|
|||
|
|
fileFilter: fileFilter,
|
|||
|
|
limits: {
|
|||
|
|
fileSize: 500 * 1024 * 1024 // 500MB
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 健康检查接口
|
|||
|
|
app.get('/health', (req, res) => {
|
|||
|
|
res.json({
|
|||
|
|
status: 'ok',
|
|||
|
|
message: '文件上传服务器运行正常',
|
|||
|
|
timestamp: new Date().toISOString()
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 上传接口
|
|||
|
|
app.post('/upload', upload.single('file'), (req, res) => {
|
|||
|
|
console.log('=== 收到上传请求 ===');
|
|||
|
|
console.log('Body:', req.body);
|
|||
|
|
console.log('File:', req.file);
|
|||
|
|
|
|||
|
|
if (!req.file) {
|
|||
|
|
console.error('没有上传文件');
|
|||
|
|
return res.json({
|
|||
|
|
status: 400,
|
|||
|
|
message: '没有上传文件'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const model = req.body.model || 'default';
|
|||
|
|
const date = new Date();
|
|||
|
|
const year = date.getFullYear();
|
|||
|
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|||
|
|
const day = String(date.getDate()).padStart(2, '0');
|
|||
|
|
|
|||
|
|
// 根据文件类型确定路径
|
|||
|
|
let modelPath = model;
|
|||
|
|
if (model === 'works' && req.file.mimetype.startsWith('video/')) {
|
|||
|
|
modelPath = 'video';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const fileUrl = `${BASE_URL}/uploads/${modelPath}/${year}/${month}/${day}/${req.file.filename}`;
|
|||
|
|
|
|||
|
|
console.log('文件上传成功:', fileUrl);
|
|||
|
|
console.log('==================');
|
|||
|
|
|
|||
|
|
res.json({
|
|||
|
|
status: 200,
|
|||
|
|
message: '上传成功',
|
|||
|
|
data: {
|
|||
|
|
url: fileUrl,
|
|||
|
|
fileName: req.file.originalname,
|
|||
|
|
fileSize: req.file.size,
|
|||
|
|
mimeType: req.file.mimetype
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 错误处理
|
|||
|
|
app.use((err, req, res, next) => {
|
|||
|
|
console.error('错误:', err.message);
|
|||
|
|
|
|||
|
|
if (err instanceof multer.MulterError) {
|
|||
|
|
if (err.code === 'LIMIT_FILE_SIZE') {
|
|||
|
|
return res.json({
|
|||
|
|
status: 400,
|
|||
|
|
message: '文件大小超过限制(最大 500MB)'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
res.json({
|
|||
|
|
status: 500,
|
|||
|
|
message: err.message || '上传失败'
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 静态文件服务
|
|||
|
|
app.use('/uploads', express.static('/data/uploads'));
|
|||
|
|
|
|||
|
|
app.listen(PORT, '0.0.0.0', () => {
|
|||
|
|
console.log('==========================================');
|
|||
|
|
console.log(`文件上传服务器运行在端口 ${PORT}`);
|
|||
|
|
console.log(`上传接口: http://0.0.0.0:${PORT}/upload`);
|
|||
|
|
console.log(`健康检查: http://0.0.0.0:${PORT}/health`);
|
|||
|
|
console.log(`文件访问: ${BASE_URL}/uploads/`);
|
|||
|
|
console.log('==========================================');
|
|||
|
|
});
|