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

React中Formik+Dropzone上传文件接口返回空对象问题

React+Formik+Dropzone文件上传为空问题修复

问题复现

大型表单普通字段提交正常,文件字段提交后为空对象,二进制文件未正确传输,请求效果截图:
文件上传为空的请求截图

核心错误原因

  • 请求格式错误:直接将FormData实例包裹在普通JS对象中传给axios,axios默认会对普通对象做JSON序列化,FormData序列化后为空对象,无法携带二进制数据;同时未按multipart/form-data格式的要求让浏览器自动生成正确的请求头。
  • 状态源不统一:Dropzone组件使用react-dropzone内部维护的acceptedFiles渲染文件列表,和Formik管理的表单状态脱节,容易出现选中文件后Formik实际未拿到正确值的问题。
  • 无效字段冗余:构造JSON提交数据时未剔除file字段,二进制文件无法被JSON序列化,属于无效传输内容。
  • 初始值类型不匹配:Formik中file初始值设为null,和Dropzone返回的数组类型不一致,容易引发取值报错。

修复方案

1. 修正提交逻辑

所有请求字段统一放入同一个FormData实例,不要拆分普通对象和FormData传输;请求时直接传FormData实例,让浏览器自动设置带正确boundary的Content-Type头,禁止手动设置multipart/form-data头(手动设置会丢失boundary参数导致后端解析失败)。

2. 统一状态源

Dropzone组件接收Formik传入的file值作为文件列表的渲染数据源,不要依赖组件内部的acceptedFiles状态,保证表单状态唯一。

3. 修正后完整代码

Formik配置与提交逻辑

<Formik
  initialValues={{
    name: "",
    // 其余表单字段
    file: [], // file初始值改为空数组,匹配Dropzone返回的数组类型
    note: "",
  }}
  onSubmit={async (values) => {
    // 先从表单值中剔除file字段,避免混入JSON序列化逻辑
    const { file, ...restFormValues } = values;
    // 构造需要JSON序列化的字段
    const jsonData = {
      ...restFormValues,
      address: JSON.stringify(values.address),
      products: JSON.stringify(values.products),
    };

    const formData = new FormData();
    // 将JSON数据转为Blob类型追加到FormData,指定正确MIME类型
    formData.append(
      "jsonData",
      new Blob([JSON.stringify(jsonData)], { type: "application/json" })
    );
    // 追加选中的文件,做空值判断避免报错
    if (file.length > 0) {
      // 单文件上传取第一个即可,多文件可遍历append
      formData.append("file", file[0]);
      // 多文件上传用下面的写法
      // file.forEach(singleFile => formData.append("file", singleFile));
    }

    // 直接传入formData实例,不要套外层普通对象
    await axios.post("https://nehra.az/api/add-supplier", formData);
  }}
>
  {({ setFieldValue, values }) => (
    <Form>
      {/* 其余普通表单项 */}
      <div className="form-input">
        {/* 传入Formik的setFieldValue和当前file值给Dropzone组件 */}
        <Dropzone setFieldValue={setFieldValue} fileValue={values.file} />
        <span className="error-span">
          <ErrorMessage name="file" />
        </span>
      </div>
      <button type="submit">提交</button>
    </Form>
  )}
</Formik>

Dropzone子组件修正

const Dropzone = ({ setFieldValue, fileValue = [] }) => {
  const { getRootProps, getInputProps, isFocused, isDragAccept, isDragReject } =
    useDropzone({
      onDrop: (acceptedFiles) => {
        // 选中文件后同步到Formik状态
        setFieldValue("file", acceptedFiles);
      },
    });

  // 统一使用Formik传入的fileValue渲染文件列表,保证状态同步
  const fileList = fileValue.map((file) => (
    <li key={file.path}>{file.path}</li>
  ));

  return (
    <div className="container">
      <div {...getRootProps({ style })}>
        <input {...getInputProps()} multiple />
        <p>点击选择文件或将文件拖拽到此处</p>
        {fileList.length > 0 ? <ul>{fileList}</ul> : null}
      </div>
    </div>
  );
};

提示:如果后端接口要求普通表单字段和文件字段平级接收,不需要嵌套jsonData字段,直接将每个普通字段单独append到FormData实例即可,比如formData.append("name", values.name)、formData.append("address", JSON.stringify(values.address)),根据后端实际参数要求调整即可。

内容的提问来源于stack exchange,提问作者Rahil Əliyev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 12:57:21