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

NextJS上传图片至外部云接口报错:Failed to Fetch

问题分析与修复方案

你的代码存在几个关键问题导致"Failed to Fetch"错误,以下是具体问题和修复步骤:

核心问题点

  • 错误的Headers使用与Content-Type设置:在Node.js环境下直接使用浏览器的Headers对象存在兼容性问题,且手动设置Content-Type: multipart/form-data会覆盖form-data自动生成的boundary标识,导致外部云存储无法正确解析请求体。
  • 文件路径拼接错误:formidable v2及以上版本中,上传文件的完整路径存在files.file.filepath字段,无需手动拼接uploadDir和newFilename。
  • 提前resolve Promise:resolve({fields, files})放在请求处理逻辑之前,逻辑顺序不合理,应该在所有处理完成后再resolve。
  • 缺少错误处理:未对fetch的响应状态进行校验,也未处理文件读取、网络请求中的异常。
  • 无效的响应发送:res.send(bodyData)尝试直接发送FormData对象,这是不合法的,且会覆盖之前的res.send(data)。
  • 未清理临时文件:上传到./public/upload的临时文件未删除,会持续占用服务器存储。

修正后的代码

import { NextApiHandler, NextApiRequest, NextApiResponse } from "next";
import formidable from "formidable";
import fs from "fs/promises";
import FormData from "form-data";

export const config = {
  api: {
    bodyParser: false,
  },
};

const readFile = async (req: NextApiRequest, res: NextApiResponse<any>) => {
  const options = {
    uploadDir: `./public/upload`,
    keepExtensions: true,
  };
  const form = formidable(options);

  return new Promise<void>((resolve, reject) => {
    form.parse(req, async (err, fields, files) => {
      if (err) {
        reject(err);
        res.status(500).json({ error: "文件解析失败" });
        return;
      }

      try {
        const uploadFile = files.file as formidable.File;
        // 使用formidable提供的完整文件路径
        const bodyData = new FormData();
        bodyData.append('files', fs.createReadStream(uploadFile.filepath));

        // 使用form-data自动生成的headers,无需手动设置Content-Type
        const result = await fetch('https://xxxxxxxxxxxxx', {
          method: 'POST',
          headers: {
            Authorization: "xxxxxxxxxxx",
            ...bodyData.getHeaders(), // 合并form-data的headers
          },
          body: bodyData,
        });

        if (!result.ok) {
          throw new Error(`云存储请求失败,状态码:${result.status}`);
        }

        const data = await result.json();
        res.status(200).json(data);

        // 清理临时文件
        await fs.unlink(uploadFile.filepath);
        resolve();
      } catch (fetchErr) {
        console.error("转传失败:", fetchErr);
        res.status(500).json({ error: "文件转传失败", details: fetchErr.message });
        reject(fetchErr);
      }
    });
  });
};

const handler: NextApiHandler = async (req, res) => {
  try {
    await readFile(req, res);
  } catch (err) {
    console.error("接口处理失败:", err);
    res.status(500).json({ error: "接口处理异常" });
  }
};

export default handler;

关键修改说明

  1. 替换Headers对象:移除浏览器的Headers,改用bodyData.getHeaders()获取form-data自动生成的包含boundary的Content-Type,确保请求格式正确。
  2. 修正文件路径:直接使用uploadFile.filepath获取完整临时文件路径,避免手动拼接错误。
  3. 调整Promise resolve时机:在文件转传完成、临时文件清理后再resolve Promise,保证逻辑完整性。
  4. 完善错误处理:添加result.ok校验HTTP响应状态,捕获并处理所有异步操作异常,返回明确错误信息。
  5. 清理临时文件:使用fs.unlink删除上传后的临时文件,避免存储泄漏。
  6. 规范响应发送:统一使用res.status().json()返回JSON格式响应,移除无效的res.send(bodyData)。

内容的提问来源于stack exchange,提问作者Flloooow

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 17:25:38