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

React Native向DRF后端发送带附件FormData报Bad Request错误

React Native 向 DRF 提交含附件表单字段丢失问题排查

我在开发React Native项目,需要向DRF后端提交带图片/PDF附件的“申请助学金”表单。使用react-native-document-picker选择文件后,通过文件URI获取Blob构造FormData发送,但后端未接收到全部字段,触发Bad Request错误。后端Web端接收数据正常,问题出在前端。

选择文件代码

const selectFile = async () => {
  // Opening Document Picker to select one file
  try {
    const res = await DocumentPicker.pickSingle({
      type: [DocumentPicker.types.allFiles],
    });
    console.log('res simple', res);
    console.log('res : ' + JSON.stringify(res));
    //fetch uri from the response
    const fileUri = res.uri;
    console.log('res : ' + fileUri);
    setSingleFile(res);

    // handleAcceptedFiles(res);
  } catch (err) {
    setSingleFile(null);
    if (DocumentPicker.isCancel(err)) {
      ToastAndroid.show('Canceled', ToastAndroid.SHORT);
    } else {
      alert('Unknown Error: ' + JSON.stringify(err));
      throw err;
    }
  }
};

获取Blob代码

useEffect(() => {
  if (singleFile != null) {
    getData();
  }
}, [singleFile]);

const getData = async () => {
  console.log('inside getData');
  // console.log("singleFile",singleFile)
  console.log('singlefile', singleFile.uri);
  await fetch(singleFile.uri)
    .then(response => {
      console.log('response', response.blob());
      // response.blob()

      setBlob(response.blob());
    })
    .catch(err => {
      console.log(err);
    });
};

上传数据代码

const uploadData = async () => {
  console.log("uploading data");
  if (blob != null) {
    const fileToUpload = singleFile;
    console.log('blob', blob);
    const data = new FormData();
    // data.append('Attachment', fileToUpload);
    data.append('Attachment', blob);

    data.append('Address', address);
    data.append('CNIC', CNIC);
    data.append('Phone_Number', number);
    data.append('Status', 'Pending Response');
    data.append('Description', description);
    data.append('Appointment_id', 'b082564a-67f0-4f3d-96d7-1a441e071ef4');
    data.append('Patient_id', email);
    data.append('Amount', '4200');

    console.log('data', data);
    // Alert('data', data);
try{


  let res= fetch(`${url}/Finance/requestFA`, {
    method: 'POST',
    headers: {
      // Accept: 'application/json',
      'Content-Type': 'multipart/form-data',
      Authorization: `Bearer ${user.refresh_token}`,
    },
    body: data,
  })
    console.log('res in fecth ', res);

      
    } catch (error) {
      console.log('error in upload', error);
    }
  } 
};

前端日志

blob {"_h": 0, "_i": 2, "_j": [TypeError: Already read], "_k": null, "_o": 0}
 LOG  data {"_parts": [["Attachment", [Promise]], ["Address", [Object]], ["CNIC", [Object]], ["Phone_Number", [Object]], ["Status", "Pending Response"], ["Description", [Object]], ["Appointment_id", "b082564a-67f0-4f3d-96d7-1a441e071ef4"], ["Patient_id", "testmobile@gmail.com"], ["Amount", "4200"]]}
 LOG  res in fecth  {"_h": 0, "_i": 0, "_j": null, "_k": null}

后端日志

<QueryDict: {'Status': ['Pending Response'], 'Appointment_id': ['b082564a-67f0-4f3d-96d7-1a441e071ef4'], 'Patient_id': ['testmobile@gmail.com'], 'Amount': ['4200']}>
'Description'
Bad Request: /Finance/requestFA

问题根源与修复方案

核心问题

  1. Blob未正确解析:response.blob()返回Promise,直接存入state导致FormData中混入未resolved的Promise,破坏表单结构,导致后续字段无法被DRF解析。
  2. 字段类型错误:前端日志显示Address、CNIC等字段是[Object]类型,DRF无法解析对象类型的表单字段,必须转为字符串。
  3. Content-Type设置错误:手动设置multipart/form-data会丢失自动生成的boundary分隔符,导致后端无法正确拆分表单数据。

修复步骤

1. 正确获取Blob

修改getData函数,等待Blob Promise完成后再存入state:

useEffect(() => {
  if (singleFile != null) {
    getData();
  }
}, [singleFile]);

const getData = async () => {
  console.log('inside getData');
  try {
    const response = await fetch(singleFile.uri);
    const blob = await response.blob();
    setBlob(blob);
  } catch (err) {
    console.log(err);
  }
};
2. 修正字段类型并调整请求头

确保所有非文件字段为字符串,并移除手动设置的Content-Type:

const uploadData = async () => {
  console.log("uploading data");
  if (blob != null) {
    const data = new FormData();
    // 添加文件名帮助后端识别文件类型
    data.append('Attachment', blob, singleFile.name);

    // 转换对象类型字段为字符串
    data.append('Address', typeof address === 'object' ? JSON.stringify(address) : address);
    data.append('CNIC', typeof CNIC === 'object' ? JSON.stringify(CNIC) : CNIC);
    data.append('Phone_Number', typeof number === 'object' ? JSON.stringify(number) : number);
    data.append('Status', 'Pending Response');
    data.append('Description', typeof description === 'object' ? JSON.stringify(description) : description);
    data.append('Appointment_id', 'b082564a-67f0-4f3d-96d7-1a441e071ef4');
    data.append('Patient_id', email);
    data.append('Amount', '4200');

    try{
      const res = await fetch(`${url}/Finance/requestFA`, {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${user.refresh_token}`,
          // 移除手动Content-Type,让系统自动生成带boundary的头
        },
        body: data,
      });
      // 解析响应查看详细结果
      const responseData = await res.json();
      console.log('res in fetch ', responseData);
    } catch (error) {
      console.log('error in upload', error);
    }
  } 
};
3. 简化方案:直接使用DocumentPicker返回的文件对象

无需转换Blob,直接用选中的文件对象构造FormData,更高效:

// 移除blob相关的useState和getData函数
const uploadData = async () => {
  console.log("uploading data");
  if (singleFile != null) {
    const data = new FormData();
    // 直接传入文件对象,自动处理URI、文件名和类型
    data.append('Attachment', {
      uri: singleFile.uri,
      name: singleFile.name,
      type: singleFile.type || 'application/octet-stream'
    });

    // 同样确保字段为字符串
    data.append('Address', typeof address === 'object' ? JSON.stringify(address) : address);
    data.append('CNIC', typeof CNIC === 'object' ? JSON.stringify(CNIC) : CNIC);
    data.append('Phone_Number', typeof number === 'object' ? JSON.stringify(number) : number);
    data.append('Status', 'Pending Response');
    data.append('Description', typeof description === 'object' ? JSON.stringify(description) : description);
    data.append('Appointment_id', 'b082564a-67f0-4f3d-96d7-1a441e071ef4');
    data.append('Patient_id', email);
    data.append('Amount', '4200');

    try{
      const res = await fetch(`${url}/Finance/requestFA`, {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${user.refresh_token}`,
        },
        body: data,
      });
      const responseData = await res.json();
      console.log('res in fetch ', responseData);
    } catch (error) {
      console.log('error in upload', error);
    }
  } 
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 12:35:28