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

React+Django Rest Framework提交裁剪图片Blob报400错误求助

解决React+Django Rest Framework上传裁剪图片的400错误问题

看起来你遇到的核心问题出在表单数据构造的异步处理和请求头配置上,咱们一步步拆解解决:

问题分析

  1. Promise直接传入FormData:你的onSubmit里,file变量是一个未resolve的Promise,FormData无法直接处理Promise类型,导致后端接收到的文件数据无效。而且你在axios.get的then里创建了File但没有return,所以Promise的最终值是undefined。
  2. 冗余的Blob URL请求:你已经在裁剪时生成了Blob,完全不需要再通过axios去请求previewUrl,直接把Blob转成File保存到状态里更高效。
  3. 错误的请求头设置:手动指定"Content-type": "multipart/form-data"会丢失自动生成的boundary参数,Django无法正确解析表单数据,这也是400错误的常见原因。

解决方案

1. 添加状态保存裁剪后的File对象

首先在组件里新增一个状态,用来存储裁剪完成后的File:

const [croppedFile, setCroppedFile] = useState(null);

2. 修改裁剪逻辑,直接生成并保存File

调整createCropPreview函数,在生成Blob后直接转为File并保存到状态,不需要再通过请求获取:

const createCropPreview = async (image, crop, fileName) => {
  const canvas = document.createElement('canvas');
  const scaleX = image.naturalWidth / image.width;
  const scaleY = image.naturalHeight / image.height;
  canvas.width = crop.width;
  canvas.height = crop.height;
  const ctx = canvas.getContext('2d');
  ctx.drawImage(
    image,
    crop.x * scaleX,
    crop.y * scaleY,
    crop.width * scaleX,
    crop.height * scaleY,
    0,
    0,
    crop.width,
    crop.height
  );

  return new Promise((resolve, reject) => {
    canvas.toBlob(blob => {
      if (!blob) {
        reject(new Error('Canvas is empty'));
        return;
      }
      // 将Blob转为File对象,指定文件名和类型
      const croppedFile = new File([blob], fileName, { type: 'image/jpeg' });
      // 更新预览URL和裁剪后的File状态
      window.URL.revokeObjectURL(previewUrl);
      const newPreviewUrl = window.URL.createObjectURL(blob);
      setPreviewUrl(newPreviewUrl);
      setCroppedFile(croppedFile);
      resolve(croppedFile);
    }, 'image/jpeg');
  });
};

3. 修复onSubmit方法,使用已保存的File对象

把onSubmit改成异步函数,直接使用状态里的croppedFile构造FormData:

const onSubmit = async (e) => {
  e.preventDefault();
  
  // 先检查是否完成裁剪
  if (!croppedFile) {
    alert('请先完成图片裁剪');
    return;
  }

  const formData = new FormData();
  formData.append('title', title);
  formData.append('headline', headline);
  formData.append('ad_text', ad_text);
  formData.append('file', croppedFile); // 直接使用状态中的File对象

  addFBFeedAd(formData);
};

4. 修正Axios请求头配置

移除手动设置的multipart/form-data,Axios会在传入FormData时自动生成正确的请求头(包含boundary):

export const tokenMultiPartConfig = (getState) => {
  const token = getState().auth.token;
  const config = {
    headers: {},
  };
  // 只添加Token认证头即可
  if (token) {
    config.headers['Authorization'] = `Token ${token}`;
  }
  return config;
};

额外检查点

  • 确保Django模型中的file字段和你FormData里的键名一致(你这里都是file,没问题)。
  • 确认Django的MEDIA_ROOT和MEDIA_URL配置正确,并且在urls.py中配置了媒体文件的访问路由。

这样修改后,应该就能正确上传裁剪后的图片了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 20:37:39