Next.js使用AWS S3 SDK V3上传文件报错TypeError: Failed To Fetch
解决Next.js + AWS SDK V3上传S3报TypeError: Failed to Fetch问题
1. 修复S3Client初始化参数错误
你的S3Client构造函数中使用了params: { Bucket: S3_BUCKET },但AWS SDK V3的S3Client并不支持该参数,这会导致客户端配置异常,进而引发请求失败。修改初始化代码:
const client = new S3Client({ region: REGION, credentials: creds, });
Bucket参数已在PutObjectCommand中指定,无需在客户端全局配置。
2. 检查并修复S3存储桶CORS配置
浏览器跨域限制是Failed to fetch的常见诱因,即使IAM权限正确,CORS规则不匹配也会阻止请求。登录AWS控制台,进入目标S3存储桶的权限 > 跨域资源共享(CORS),添加以下规则(根据前端实际域名调整):
[ { "AllowedHeaders": ["*"], "AllowedMethods": ["PUT", "POST", "GET"], "AllowedOrigins": ["https://your-production-domain.com", "http://localhost:3000"], "ExposeHeaders": ["ETag"] } ]
开发环境需保留http://localhost:3000,生产环境替换为实际域名。
3. 禁止前端直接暴露AWS密钥(核心修正)
你使用NEXT_PUBLIC_前缀的环境变量会被打包到前端代码中,直接暴露AWS密钥存在严重安全风险。正确做法是通过Next.js API路由作为中间层,在后端处理S3上传:
步骤1:创建API路由(pages/api/upload-s3.js)
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3"; import { v4 as uuidv4 } from "uuid"; export const config = { api: { bodyParser: false, // 禁用默认解析,适配文件上传 }, }; const client = new S3Client({ region: process.env.AWS_REGION, credentials: { accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, }, }); export default async function handler(req, res) { if (req.method !== "POST") { return res.status(405).json({ message: "Method not allowed" }); } const formidable = await import("formidable"); const form = formidable.default({}); form.parse(req, async (err, fields, files) => { if (err) { return res.status(500).json({ message: "Failed to parse form" }); } const file = files.file[0]; const fileType = file.originalFilename.split(".").pop(); const fileId = `${uuidv4()}.${fileType}`; try { const command = new PutObjectCommand({ Body: file.filepath, Bucket: process.env.AWS_BUCKET_NAME, Key: fileId, }); await client.send(command); return res.status(200).json({ key: fileId }); } catch (err) { console.error(err); return res.status(500).json({ message: "Failed to upload to S3" }); } }); }
步骤2:前端修改上传逻辑,调用API路由
const handleUploadFiles = async (e) => { if (!e.target.files.length) return; const promises = Array.from(e.target.files).map(async (file) => { setDocCounter(prev => prev + 1); const formData = new FormData(); formData.append("file", file); try { const response = await fetch("/api/upload-s3", { method: "POST", body: formData, }); const data = await response.json(); console.log("Uploaded:", data.key); } catch (err) { console.error(err); } }); await Promise.all(promises); setSnackbarMessage("Invoices successfully uploaded"); setUploadSnackbarOpen(true); };
步骤3:更新环境变量
移除NEXT_PUBLIC_前缀,将密钥放在.env.local中(不会暴露到前端):
AWS_ACCESS_KEY_ID=your-access-key AWS_SECRET_ACCESS_KEY=your-secret-key AWS_BUCKET_NAME=your-bucket-name AWS_REGION=your-region
4. 清除Next.js缓存并重启服务
旧环境变量可能被缓存,导致新密钥未生效。执行以下操作:
# 开发环境 rm -rf .next && npm run dev # 生产环境 npm run build && npm start
5. 开启SDK日志排查请求细节
若以上步骤无效,可开启AWS SDK日志功能,查看请求签名、URL等详情:
const client = new S3Client({ region: REGION, credentials: creds, logger: console, // 开启日志输出 });
对比控制台日志与CLI请求的差异,定位问题点。
内容的提问来源于stack exchange,提问作者Rich
相关产品推荐
相关产品推荐

