const crypto = require('crypto') const fs = require('fs') const path = require('path') const multer = require('multer') const { ok, fail } = require('./http') const uploadDir = process.env.UPLOAD_DIR ? path.resolve(process.env.UPLOAD_DIR) : path.join(__dirname, '..', 'public', 'uploads') const IMAGE_RE = /^image\/(png|jpe?g|webp)$/i const VIDEO_RE = /^video\/(mp4|quicktime|webm)$/i const storage = multer.diskStorage({ destination(req, file, cb) { fs.mkdirSync(uploadDir, { recursive: true }) cb(null, uploadDir) }, filename(req, file, cb) { const ext = path.extname(file.originalname || '').toLowerCase() || '.bin' cb(null, `${Date.now().toString(36)}${crypto.randomBytes(3).toString('hex')}${ext}`) } }) function uploader(options) { const imagesOnly = !!(options && options.imagesOnly) const middleware = multer({ storage, limits: { fileSize: imagesOnly ? 5 * 1024 * 1024 : 80 * 1024 * 1024 }, fileFilter(req, file, cb) { const mime = file.mimetype || '' const allowed = IMAGE_RE.test(mime) || (!imagesOnly && VIDEO_RE.test(mime)) cb(null, allowed) } }).single('file') return (req, res) => { middleware(req, res, (error) => { if (error) { const tooLarge = error.code === 'LIMIT_FILE_SIZE' return fail(res, 400, tooLarge ? (imagesOnly ? '图片需小于 5MB' : '文件需小于 80MB') : '上传失败') } if (!req.file) { return fail(res, 400, imagesOnly ? '仅支持 png/jpg/webp 图片' : '仅支持 png/jpg/webp 图片或 mp4/mov/webm 视频') } const type = VIDEO_RE.test(req.file.mimetype) ? 'video' : 'image' ok(res, { url: `/static/uploads/${req.file.filename}`, type }) }) } } module.exports = { uploader, uploadDir }