首页菜鸟笔记前端使用axios或fetch上传文件,服务端报错:the request was rejected because no multipart boundary was found
Created At : 2022-01-05
Last Updated: 2022-01-05

前端使用axios或fetch上传文件,服务端报错:the request was rejected because no multipart boundary was found

当前端使用表单上传文件,但是使用Axios或Fetch发送请求时,如果使用不当,会报错:Failed to parse multipart servlet request; nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found 前端代码如下:

前后端代码及报错信息

 <input type="file" class="hidden-upload-input" @change="uploadCoverHandle"  name="file" accept="image/*" enctype="multipart/form-data"/>


 const uploadCoverHandle = (e:Event)=>{
  var formData = new FormData();
  //@ts-ignore
  formData.append( 'file',e.target.files[0])
  console.log(e)
  //useUploadClient("/common/qiniu/uploadFileByServe",formData)
  fetch(url, {
    url,
    body:formData,
    method:'POST',
    headers: {
      'Content-Type': 'multipart/form-data'
    }
  })
}

后端代码如下:

 @PostMapping("/uploadFileByServe")
    public R<QiniuPutRet> uploadFileByServe(@RequestParam("file")  
        MultipartFile file) throws IOException {
        log.info("--------------uploadFile-" + file.toString());
        log.info(file.getOriginalFilename() + "=" + file.getName() + "=" + file.getSize());
        InputStream inputStream = file.getInputStream();
        Response res = uploadManager.put(inputStream, null, getUploadToken(),null,null);
        // 解析上传成功的结果
        QiniuPutRet qiniuPutRet = res.jsonToObject(QiniuPutRet.class);
        log.info(qiniuPutRet.toString());
        qiniuPutRet.setDomain(qiniuConfig.getDomain());
        return R.success(qiniuPutRet);
    }

会发现后端报上述错误。

原因探究

因为,当我们使用HTTP form表单进行文件上传时,请求数据实际上根据参数分片封装的,大致的数据格式如下:

<form method="post" action="/upload" enctype="multipart/form-data">
    <input type="file"  name="file" accept="image/*" />
    <button type="submit">Submit</button>
</form>
//数据格式
------WebKitFormBoundaryKrjTL2X3JXEobJYG
Content-Disposition: form-data; name="file"; filename="wx_souyisou.png"
Content-Type: image/png
// 请求头
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarycAmE6ANaU471at2V

可以看到,在"multipart/form-data"后面附带了一个boundary属性。 这个boundary的值就是请求数据中的分隔符,服务器就是根据这个分隔符,将数据拆分为多个参数,然后分别解析其数据的。

如果需要自己构造这个boundary属性,可以参考:

https://developer.mozilla.org/zh-CN/docs/Learn/Forms/Sending_forms_through_JavaScript

当使用form表单进行文件上传时,浏览器会自动为我们附加这个boundary属性。

即数据格式类似如下:

POST /test.html HTTP/1.1
Host: example.org
Content-Type: multipart/form-data;boundary="boundary"

--boundary
Content-Disposition: form-data; name="field1"

value1
--boundary
Content-Disposition: form-data; name="field2"; filename="example.txt"

value2

但是,当我们使用ajax上传时(如axios,fetch),如果没有自己构造boundary属性,默认的请求数据如下:

//请求头
Content-Type: multipart/form-data
// 请求负载
------WebKitFormBoundaryKrjTL2X3JXEobJYG
Content-Disposition: form-data; name="file"; filename="wx_souyisou.png"
Content-Type: image/png


------WebKitFormBoundaryKrjTL2X3JXEobJYG--

发现,并没有boundary属性, 上述数据是以Payload负载的形式传送的。

解决方案

⚡ 其实解决办法也很简单,请求头中不要设置'Content-Type': 'multipart/form-data' 即可.

代码修改如下:

fetch(url, {
    url,
    body:formData,
    method:'POST',
    headers: {
      // 不要设置  'Content-Type': 'multipart/form-data', 或设置为 false
      //'Content-Type': 'multipart/form-data'
    }
})

因为一旦我们设置了该属性,浏览器和fetch将不会再为我们自动附加这个boundary。