求基于NextJS 13.4 App Router与AWS SDK v3的S3预签名URL上传示例
NextJS 13.4 App Router + AWS SDK v3 S3预签名URL上传示例
1. 安装依赖
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
2. 配置环境变量
在项目根目录创建.env.local文件,添加以下内容:
AWS_ACCESS_KEY_ID=你的AWS访问密钥ID AWS_SECRET_ACCESS_KEY=你的AWS秘密访问密钥 AWS_REGION=你的S3桶所在区域(比如us-east-1) S3_BUCKET_NAME=目标S3桶名称
3. 创建App Router API路由
新建app/api/s3/presigned-url/route.ts,实现预签名URL生成接口:
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; import { NextResponse } from "next/server"; const s3Client = new S3Client({ region: process.env.AWS_REGION!, credentials: { accessKeyId: process.env.AWS_ACCESS_KEY_ID!, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!, }, }); export async function POST(request: Request) { try { const { fileName, fileType } = await request.json(); const command = new PutObjectCommand({ Bucket: process.env.S3_BUCKET_NAME!, Key: fileName, // 可自定义存储路径,比如`uploads/${fileName}` ContentType: fileType, }); // 生成有效期15分钟的预签名URL const signedUrl = await getSignedUrl(s3Client, command, { expiresIn: 900 }); return NextResponse.json({ signedUrl }); } catch (error) { console.error("生成预签名URL失败:", error); return NextResponse.json({ error: "生成预签名URL失败" }, { status: 500 }); } }
4. 前端上传组件示例
创建一个简单的上传组件components/S3Uploader.tsx:
"use client"; import { useState } from "react"; export default function S3Uploader() { const [file, setFile] = useState<File | null>(null); const [uploading, setUpLoading] = useState(false); const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => { if (e.target.files?.[0]) { setFile(e.target.files[0]); } }; const handleUpload = async () => { if (!file) return; setUpLoading(true); try { // 第一步:获取预签名URL const presignRes = await fetch("/api/s3/presigned-url", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ fileName: file.name, fileType: file.type, }), }); const { signedUrl } = await presignRes.json(); // 第二步:用预签名URL上传文件到S3 const uploadRes = await fetch(signedUrl, { method: "PUT", body: file, headers: { "Content-Type": file.type }, }); if (uploadRes.ok) { alert("文件上传成功"); setFile(null); } else { throw new Error("S3上传失败"); } } catch (error) { console.error("上传失败:", error); alert("上传失败,请重试"); } finally { setUpLoading(false); } }; return ( <div> <input type="file" onChange={handleFileChange} disabled={uploading} /> <button onClick={handleUpload} disabled={!file || uploading}> {uploading ? "上传中..." : "上传到S3"} </button> </div> ); }
关键注意事项
- S3桶CORS配置:需在S3控制台配置CORS规则,允许PUT请求:
[ { "AllowedHeaders": ["*"], "AllowedMethods": ["PUT"], "AllowedOrigins": ["你的NextJS域名,比如http://localhost:3000"], "ExposeHeaders": [] } ] - IAM权限:确保你的AWS IAM用户拥有
s3:PutObject权限,策略示例:{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::你的桶名称/*" } ] } - 安全建议:生产环境中避免直接暴露IAM密钥,可考虑使用AWS IAM Roles for Service Accounts(如果部署在ECS/EKS)或Vercel的环境变量加密;同时可对文件名添加前缀(比如用户ID)避免冲突,限制允许上传的文件类型和大小。
内容的提问来源于stack exchange,提问作者Chris Valentine
相关产品推荐
相关产品推荐

