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

Cloudflare Images上传时出现CORS错误的解决求助

问题解决:Cloudflare Images 上传的 CORS 与 415 错误处理

核心结论:直接浏览器请求必然触发 CORS

Cloudflare Images 的 API 是为后端服务调用设计的,官方不会配置允许前端域名的 CORS 响应头,所以浏览器端直接用 fetch/axios 请求一定会触发 CORS 拦截。不存在安全且合规的绕过方式,强行绕过(比如第三方代理)会暴露你的 API Key,带来严重安全风险,绝对不推荐。

解决 Next.js 中转的 415 错误

415 错误是因为请求的媒体类型不匹配,本质是你构造 FormData 和请求头的方式有误。以下是修正方案:

1. 正确解析 Next.js API 中的上传文件

Next.js 的 API 路由默认不会处理文件上传,需要用对应方式解析:

Pages Router 方案(依赖 formidable)

先安装依赖:

npm install formidable

API 路由代码:

import formidable from 'formidable';
import axios from 'axios';
import fs from 'fs';

export const config = {
  api: {
    bodyParser: false, // 禁用默认解析,交给formidable处理
  },
};

export default async function handler(req, res) {
  if (req.method !== 'POST') return res.status(405).json({ error: '仅支持POST请求' });

  const form = formidable({});
  const [fields, files] = await form.parse(req);
  const imageFile = files.image?.[0];

  if (!imageFile) return res.status(400).json({ error: '未上传图片文件' });

  // 构造传给Cloudflare的FormData
  const cloudflareForm = new FormData();
  cloudflareForm.append('file', fs.createReadStream(imageFile.filepath), {
    filename: imageFile.originalFilename,
    contentType: imageFile.mimetype,
  });

  try {
    const response = await axios.post(
      `https://api.cloudflare.com/client/v4/accounts/${process.env.CLOUDFLARE_ACCOUNT_ID}/images/v1`,
      cloudflareForm,
      {
        headers: {
          Authorization: `Bearer ${process.env.CLOUDFLARE_API_KEY}`,
          // 不要手动设置Content-Type,axios会自动生成multipart/form-data及boundary
        },
      }
    );
    res.status(200).json(response.data);
  } catch (err) {
    res.status(err.response?.status || 500).json({
      error: err.response?.data || '上传失败',
    });
  }
}

App Router 方案(内置 formData 解析)

import { NextResponse } from 'next/server';
import axios from 'axios';

export async function POST(req) {
  const formData = await req.formData();
  const imageFile = formData.get('image');

  if (!imageFile) return NextResponse.json({ error: '未上传图片文件' }, { status: 400 });

  const cloudflareForm = new FormData();
  cloudflareForm.append('file', imageFile);

  try {
    const response = await axios.post(
      `https://api.cloudflare.com/client/v4/accounts/${process.env.CLOUDFLARE_ACCOUNT_ID}/images/v1`,
      cloudflareForm,
      {
        headers: {
          Authorization: `Bearer ${process.env.CLOUDFLARE_API_KEY}`,
        },
      }
    );
    return NextResponse.json(response.data);
  } catch (err) {
    return NextResponse.json(
      { error: err.response?.data || '上传失败' },
      { status: err.response?.status || 500 }
    );
  }
}

2. 原代码的问题点

  • 错误解析请求体:new Blob(req.body.image) 无效,因为未解析的 req.body 不是二进制文件数据,导致构造的 FormData 格式错误。
  • 手动干扰请求头:axios 发送 FormData 时会自动设置正确的 Content-Type,手动设置反而会破坏格式。

关于 Next.js 执行 curl 命令的方案

完全没必要,直接用 HTTP 库(axios/node-fetch)就可以实现相同效果。如果非要用 curl,可通过 child_process 调用,但出错处理更繁琐:

import { exec } from 'child_process';
import { promisify } from 'util';
import formidable from 'formidable';

const execPromise = promisify(exec);

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

export default async function handler(req, res) {
  const form = formidable({});
  const [fields, files] = await form.parse(req);
  const imageFile = files.image?.[0];

  if (!imageFile) return res.status(400).json({ error: '未上传图片文件' });

  const curlCmd = `curl -X POST "https://api.cloudflare.com/client/v4/accounts/${process.env.CLOUDFLARE_ACCOUNT_ID}/images/v1" \
    -H "Authorization: Bearer ${process.env.CLOUDFLARE_API_KEY}" \
    -F "file=@${imageFile.filepath};filename=${imageFile.originalFilename}"`;

  try {
    const { stdout, stderr } = await execPromise(curlCmd);
    if (stderr) throw new Error(stderr);
    res.status(200).json(JSON.parse(stdout));
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 14:52:48