zhibo/upload-server-example/server.js

152 lines
4.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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('==========================================');
});