Node.js+TypeScript+Multer S3上传报错:Cannot read properties of undefined (reading 'map')
问题:整合S3上传逻辑到住宿控制器后出现
Cannot read properties of undefined (reading 'map')错误 我原本实现了一个可正常将文件上传至S3存储桶的独立路由,但将该上传逻辑整合到Accommodation控制器并添加额外业务逻辑后,出现错误:Cannot read properties of undefined (reading 'map')。我在Postman中对两个路由使用了完全相同的请求,求排查问题所在。
相关代码
原上传控制器逻辑
import { Request, Response } from "express"; import catchBlock from "../utils/catchBlock"; import { s3Uploadv2 } from "../utils/s3Service"; const UploadController = { Upload: async (req: Request, res: Response) => { const files = req.files as Express.Multer.File[]; try { const results = await s3Uploadv2(files); res.send({ success: "successful", results }); } catch (e: unknown) { catchBlock(e, res); } }, }; export default UploadController;
住宿模块上传逻辑
UploadImages: async (req: Request, res: Response) => { const accommodationId = req.params.id; const accommodation = await accommodationSchema.findOne({ _id: accommodationId, }); const files = req.files as Express.Multer.File[]; try { const results = await s3Uploadv2(files); console.log(results); if (accommodation) { results.forEach((file) => accommodation.photos.push(file.Location)); await accommodation.save(); res.send({ success: "successful", accommodation }); } else { res.status(400).send("No such accommodation"); } res.send({ success: "successful", results }); } catch (e: unknown) { catchBlock(e, res); } },
S3服务代码
import { S3 } from "aws-sdk"; import { v4 as uuid } from "uuid"; export interface Param { Bucket: string; Key: string; Body: Buffer; } export const s3Uploadv2 = async (files: Express.Multer.File[]) => { const s3 = new S3(); const params: Param[] = files.map((file) => { return { Bucket: process.env.AWS_BUCKET_NAME, Key: `uploads/${uuid()}-${file.originalname}`, Body: file.buffer, }; }); const results = await Promise.all( params.map((param) => s3.upload(param).promise()) ); return results; };
Multer服务代码
import multer, { FileFilterCallback, MulterError } from "multer"; import { Request } from "express"; const storage = multer.memoryStorage(); const fileFilter = ( req: Request, file: Express.Multer.File, cb: FileFilterCallback ) => { if (file.mimetype.split("/")[0] === "image") { cb(null, true); } else { cb(new MulterError("LIMIT_UNEXPECTED_FILE")); } }; export const upload = multer({ storage, fileFilter, limits: { fileSize: 1000000, files: 5 }, });
住宿模块路由
import express from "express"; import AccommodationController from "../controllers/accommodation"; const accommodationRouter = express.Router(); accommodationRouter.get("/", AccommodationController.All); accommodationRouter.post( "/create", AccommodationController.CreateAccommodation ); accommodationRouter.get( "/users-accommodation", AccommodationController.UsersAccommodation ); accommodationRouter.post("/delete/:id", AccommodationController.Delete); accommodationRouter.post("/upload/:id", AccommodationController.UploadImages); export default accommodationRouter;
上传模块路由
import express from "express"; import UploadController from "../controllers/upload"; import { upload } from "../utils/multer"; const uploadRouter = express.Router(); uploadRouter.post("/", upload.array("file", 5), UploadController.Upload); export default uploadRouter;
问题排查与修复
核心错误原因
你在住宿模块的上传路由中没有挂载multer的upload.array("file",5)中间件。原上传路由正是通过这个中间件解析上传的文件,并把解析后的文件列表挂载到req.files上;而住宿模块的路由直接调用控制器,导致req.files为undefined,当s3Uploadv2函数执行files.map时,就会抛出Cannot read properties of undefined (reading 'map')的错误。
修复步骤
修改住宿模块的路由,在UploadImages控制器前添加multer中间件:
// 先导入multer的upload实例 import { upload } from "../utils/multer"; // 修改上传路由 accommodationRouter.post("/upload/:id", upload.array("file",5), AccommodationController.UploadImages);
额外注意事项
- 检查你的
accommodationSchema中photos字段是否已初始化为数组类型(比如photos: { type: [String], default: [] }),避免accommodation.photos为undefined导致push报错。 - 住宿控制器的
UploadImages方法中存在双重响应的问题:在if(accommodation)分支里执行了res.send,之后又执行了一次res.send,这会导致Express抛出Cannot set headers after they are sent to the client的错误,需要移除其中一个响应语句。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

