如何在NextJS后端API路由中通过AWS Amplify Gen 2上传图片?
在Next.js后端API路由中实现AWS Amplify Gen2文件上传(带事务+认证解决)
问题背景
在NextJS项目中使用AWS Amplify Gen 2时,需在后端API路由中完成图片上传与数据库更新的原子事务(若上传失败则数据库操作回滚)。尝试使用前端的aws-amplify/storage包中uploadData函数时,因后端缺少前端Authenticator上下文,抛出Unauthorized, Access Denied错误,且官方文档无后端上传示例。
报错代码
import { uploadData } from "aws-amplify/storage"; import { db } from "@/lib/db"; // 假设你的Prisma连接导出在这里 export async function POST(req: Request) { try { const formData = await req.formData(); const image = formData.get("image") as File | null; const prisma = await db(); if (image) { const filename = image.name; const result = await prisma.$transaction(async (tx) => { const dbResult = await tx.image.create({ data: { name: image.name, }, }); await uploadData({ data: image, path: `images/${filename}`, }); return dbResult; }); return Response.json( { message: "Image uploaded", result }, { status: 200 } ); } return Response.json({ message: "No image provided" }, { status: 400 }); } catch (error) { console.error(error); return Response.json({ error: "Internal server error" }, { status: 500 }); } }
解决方案
后端无法直接使用前端的uploadData函数(依赖前端认证上下文),需改用AWS SDK v3直接操作S3,并通过服务器端凭证完成认证。
1. 安装AWS SDK依赖
npm install @aws-sdk/client-s3 @aws-sdk/lib-storage
2. 配置后端S3客户端
利用Amplify Gen2自动注入的环境变量(本地开发通过amplify pull获取,部署时Amplify会自动配置IAM角色权限),初始化S3客户端:
import { S3Client } from "@aws-sdk/client-s3"; const s3Client = new S3Client({ region: process.env.AWS_REGION, // 本地开发时Amplify自动注入凭证,部署时无需手动配置(使用IAM角色) credentials: process.env.NODE_ENV === "development" ? { accessKeyId: process.env.AWS_ACCESS_KEY_ID!, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!, } : undefined, });
3. 修改API路由代码(整合事务与S3上传)
使用@aws-sdk/lib-storage中的Upload类处理文件流式上传,并将数据库操作与上传包裹在Prisma事务中:
import { Upload } from "@aws-sdk/lib-storage"; import { S3Client } from "@aws-sdk/client-s3"; import { db } from "@/lib/db"; const s3Client = new S3Client({ region: process.env.AWS_REGION, credentials: process.env.NODE_ENV === "development" ? { accessKeyId: process.env.AWS_ACCESS_KEY_ID!, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!, } : undefined, }); export async function POST(req: Request) { try { const formData = await req.formData(); const image = formData.get("image") as File | null; const prisma = await db(); if (!image) { return Response.json({ message: "No image provided" }, { status: 400 }); } const filename = image.name; const bucketName = process.env.AMPLIFY_STORAGE_BUCKET_NAME; // Amplify存储桶名称,从环境变量获取 const result = await prisma.$transaction(async (tx) => { // 1. 写入数据库 const dbResult = await tx.image.create({ data: { name: filename, }, }); // 2. 将File转换为Buffer const fileBuffer = await image.arrayBuffer(); // 3. 上传到S3 const upload = new Upload({ client: s3Client, params: { Bucket: bucketName, Key: `images/${filename}`, Body: Buffer.from(fileBuffer), ContentType: image.type, }, }); await upload.done(); return dbResult; }); return Response.json( { message: "Image uploaded", result }, { status: 200 } ); } catch (error) { console.error("Upload failed:", error); return Response.json({ error: "Internal server error" }, { status: 500 }); } }
4. 权限配置
- 本地开发:执行
amplify pull拉取最新后端配置,Amplify会自动注入正确的凭证环境变量。 - 部署到Amplify:在Amplify控制台「权限」页面,为后端API的IAM角色添加S3存储桶的
PutObject权限(可通过CLI或控制台策略编辑器配置)。
内容的提问来源于stack exchange,提问作者Amal Gunatilake
相关产品推荐
相关产品推荐

