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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 10:17:03