Next.js 14中S3文件上传URL存入Mongoose模型的实现问题
解决S3上传URL获取与Mongoose存储问题及multer-s3示例
一、获取S3上传文件的URL
你需要修改uploadFileToS3函数,让它返回文件的访问URL,分两种场景实现:
场景1:公开可读S3桶
如果桶配置了公开读取权限,上传后可直接构造固定URL:
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3"; const s3Client = new S3Client({ region: process.env.AWS_REGION, credentials: { accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, }, }); async function uploadFileToS3(buffer, fileName, contentType, folderName) { const bucketName = process.env.AWS_S3_BUCKET_NAME; const key = `${folderName}/${fileName}`; const command = new PutObjectCommand({ Bucket: bucketName, Key: key, Body: buffer, ContentType: contentType, ACL: "public-read" // 公开桶必须设置该权限 }); await s3Client.send(command); // 返回公开访问URL return `https://${bucketName}.s3.${process.env.AWS_REGION}.amazonaws.com/${key}`; }
场景2:私有S3桶(生成签名临时URL)
如果桶是私有的,生成带过期时间的签名URL:
import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; const s3Client = new S3Client({ /* 配置同上 */ }); async function uploadFileToS3(buffer, fileName, contentType, folderName) { const bucketName = process.env.AWS_S3_BUCKET_NAME; const key = `${folderName}/${fileName}`; const putCommand = new PutObjectCommand({ Bucket: bucketName, Key: key, Body: buffer, ContentType: contentType, }); await s3Client.send(putCommand); // 生成1小时有效期的签名URL const getCommand = new GetObjectCommand({ Bucket: bucketName, Key: key }); const signedUrl = await getSignedUrl(s3Client, getCommand, { expiresIn: 3600 }); return signedUrl; }
二、将S3 URL存入Mongoose模型
修改你的/api/s3/route.js,在上传成功后获取URL,并存入Team模型:
import { NextResponse } from "next/server"; import { Team } from "@/models/Team"; // 替换为你的模型实际路径 import mongoose from "mongoose"; // 确保MongoDB连接 async function connectDB() { if (mongoose.connections[0].readyState) return; await mongoose.connect(process.env.MONGODB_URI); } export async function POST(request) { try { await connectDB(); const formData = await request.formData(); const file = formData.get("file"); const teamId = formData.get("teamId"); // 表单需传入teamId(更新场景) if (!file) { return NextResponse.json({ error: "No file found" }, { status: 400 }); } const buffer = Buffer.from(await file.arrayBuffer()); const fileName = file.name; const contentType = file.type; let folderName = ""; if (contentType.includes("image")) { folderName = "image"; } else if (contentType === "application/pdf") { folderName = "pdf"; } else { return NextResponse.json( { error: "Unsupported file type" }, { status: 400 } ); } // 获取S3文件URL const fileUrl = await uploadFileToS3(buffer, fileName, contentType, folderName); // 存入Mongoose:根据需求选择创建或更新 if (teamId) { await Team.findByIdAndUpdate(teamId, { Photo: fileUrl }); } else { await Team.create({ Photo: fileUrl }); } return NextResponse.json({ success: true, fileName, fileUrl }); } catch (error) { console.error(error); return NextResponse.json( { error: "Error uploading file and saving to DB" }, { status: 500 } ); } }
三、multer-s3实现URL获取示例
如果需要用multer-s3(适配Next.js API路由),步骤如下:
- 安装依赖:
npm install multer multer-s3 aws-sdk next-connect
- 创建
/api/s3-multer/route.js:
import nextConnect from "next-connect"; import multer from "multer"; import multerS3 from "multer-s3"; import AWS from "aws-sdk"; import { Team } from "@/models/Team"; import mongoose from "mongoose"; // 配置AWS S3 AWS.config.update({ accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, region: process.env.AWS_REGION, }); const s3 = new AWS.S3(); // 配置multer-s3 const upload = multer({ storage: multerS3({ s3: s3, bucket: process.env.AWS_S3_BUCKET_NAME, acl: "public-read", // 公开桶设置,私有桶移除该配置 key: (req, file, cb) => { const folderName = file.mimetype.includes("image") ? "image" : "pdf"; // 添加时间戳避免文件重名 cb(null, `${folderName}/${Date.now()}-${file.originalname}`); }, }), fileFilter: (req, file, cb) => { if (file.mimetype.includes("image") || file.mimetype === "application/pdf") { cb(null, true); } else { cb(new Error("Unsupported file type")); } }, }); const handler = nextConnect(); // 处理单文件上传 handler.use(upload.single("file")); handler.post(async (req, res) => { try { await mongoose.connect(process.env.MONGODB_URI); // 从multer-s3获取文件URL const fileUrl = req.file.location; // 存入Mongoose const teamId = req.body.teamId; if (teamId) { await Team.findByIdAndUpdate(teamId, { Photo: fileUrl }); } else { await Team.create({ Photo: fileUrl }); } res.status(200).json({ success: true, fileUrl }); } catch (error) { console.error(error); res.status(500).json({ error: "Error processing upload" }); } }); // 禁用Next.js默认bodyParser,让multer处理表单数据 export const config = { api: { bodyParser: false, }, }; export default handler;
内容的提问来源于stack exchange,提问作者MD. Hasanur Rahman
相关产品推荐
相关产品推荐

