You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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路由),步骤如下:

  1. 安装依赖:
npm install multer multer-s3 aws-sdk next-connect
  1. 创建/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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 07:43:19