import multer from 'multer'; import fs from 'node:fs'; import path from 'node:path'; import { callNodeListener } from 'h3'; import { RELATIVE_ASSETS_DIR, POST_MEDIA_PUBLIC_PREFIX, ALLOWED_MIME_TYPES, MAX_FILE_SIZE, MAX_FILE_COUNT, } from '#server/constants/upload'; import { getContextUser } from '#server/utils/context'; interface IFile { name: string; url: string; mimeType: string; size: number; } export default defineWrappedResponseHandler({ auth: 'required' }, async (event) => { const user = getContextUser(event)!; const uploadDir = path.resolve(RELATIVE_ASSETS_DIR); if (!fs.existsSync(uploadDir)) { fs.mkdirSync(uploadDir, { recursive: true }); } const storage = multer.diskStorage({ destination: uploadDir, filename: (_req, file, cb) => { const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9); const ext = path.extname(file.originalname).toLowerCase(); const baseName = path.basename(file.originalname, ext).replace(/[^a-z0-9]/gi, '-'); cb(null, `${uniqueSuffix}-${baseName}${ext}`); }, }); const upload = multer({ storage, limits: { fileSize: MAX_FILE_SIZE }, fileFilter: (_req, file, cb) => { if ((ALLOWED_MIME_TYPES as readonly string[]).includes(file.mimetype)) { cb(null, true); } else { cb(new Error('只支持 PNG/JPG/WebP 格式图片')); } }, }); await callNodeListener( // @ts-expect-error Nuxt 类型兼容 upload.array('file', MAX_FILE_COUNT), event.node.req, event.node.res, ); // @ts-expect-error const uploadedFiles = event.node.req.files || []; const result: IFile[] = uploadedFiles.map((file: any) => ({ name: file.originalname, url: `${POST_MEDIA_PUBLIC_PREFIX}${file.filename}`, mimeType: file.mimetype, size: file.size, })); return result; });