import multer from 'multer'; import fs from 'node:fs'; import path from 'node:path'; import { callNodeListener } from 'h3'; import { R } from '#server/utils/response'; // 类型定义 interface IFile { name: string; url: string; // 前端可直接访问的 URL path: string; // 服务器存储路径 mimeType: string; size: number; } export default defineWrappedResponseHandler(async (event) => { try { // 存储目录 const uploadDir = path.join(process.cwd(), 'public/assets'); // 自动创建目录 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, '-'); // 最终文件名(带后缀) const filename = `${uniqueSuffix}-${baseName}${ext}`; cb(null, filename); }, }); // 上传配置 const upload = multer({ storage, limits: { fileSize: 5 * 1024 * 1024, // 5MB 限制 }, fileFilter: (req, file, cb) => { const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/webp']; if (allowedTypes.includes(file.mimetype)) { cb(null, true); } else { cb(new Error('只支持 PNG/JPG/WebP 格式图片')); } }, }); // 执行上传(最多 10 个文件) await callNodeListener( // @ts-expect-error Nuxt 类型兼容 upload.array('file', 10), event.node.req, event.node.res ); // 获取上传后的文件 // @ts-expect-error const uploadedFiles = event.node.req.files || []; if (!Array.isArray(uploadedFiles) || uploadedFiles.length === 0) { throw createError({ statusCode: 400, statusMessage: '请选择要上传的图片' }); } // 格式化返回数据(与 `public` 静态中间件路径一致:`/public/assets/...`) const result: IFile[] = uploadedFiles.map((file: { originalname: string; filename: string; path: string; mimetype: string; size: number }) => ({ name: file.originalname, url: `/public/assets/${file.filename}`, path: file.path, mimeType: file.mimetype, size: file.size, })); return R.success({ files: result }); } catch (err: unknown) { console.error('上传失败:', err); if (err && typeof err === 'object' && 'statusCode' in err) { throw err; } const msg = err instanceof Error ? err.message : '上传失败'; throw createError({ statusCode: 400, statusMessage: msg }); } });