获取 - multipart/form-data POST 中缺少边界

126

我想使用fetch API,将一个new FormData()作为POST请求的body发送。

操作大致如下:

var formData = new FormData()
formData.append('myfile', file, 'someFileName.csv')

fetch('https://api.myapp.com', 
  {
    method: 'POST',
    headers: {
      "Content-Type": "multipart/form-data"
    },
    body: formData
  }
)

问题在于边界,就像某件事情

boundary=----WebKitFormBoundaryyEmKNDsBKjB7QEqu

从来没有进入Content-Type:头部

应该看起来像这样:

Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryyEmKNDsBKjB7QEqu

当您尝试使用new XMLHttpRequest()进行“相同”操作时,如下所示:

var request = new XMLHttpRequest()
request.open("POST", "https://api.mything.com")
request.withCredentials = true
request.send(formData)

标题已经正确设置。

Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryyEmKNDsBKjB7QEqu

那么我的问题是:

  1. 在这种情况下,我如何使 fetch 的行为与 XMLHttpRequest 完全相同?
  2. 如果不可能,请问为什么?

谢谢大家!这个社区或多或少是我取得职业成功的原因。

7个回答

224
问题的解决方案是明确设置 Content-Typeundefined,这样你使用的浏览器或其他客户端就可以设置它,并为你添加那个分界值。令人失望但却是真实的。

36
使用 fetch,我移除了 Content-Type 头部,然后它就可以工作了。 - sww314
29
太不可思议了!我花了好几个小时才找到这个解决方法!!delete result.headers['Content-Type'];对我有用,谢谢! - Crysfel
3
谢谢,删除内容类型对我有帮助 :P - master_dodo
11
对我来说不起作用。将其设置为未定义会默认为文本/纯文本。 - sebnukem
6
在使用fetch进行POST请求之前,将content-type设置为undefined或删除content-type并不能解决我的问题... - Ahab
显示剩余14条评论

22
我曾遇到同样的问题,通过排除 Content-Type 属性,让浏览器自动检测和设置边界和内容类型,我成功地解决了这个问题。

你的代码变成了:

var formData = new FormData()
formData.append('myfile', file, 'someFileName.csv')

fetch('https://api.myapp.com',
  {
    method: 'POST',
    body: formData
  }
)

19

我删除了 "Content-Type",并在HTTP头中添加了 'Accept',对我很有用。 这是我使用的头文件,

'headers': new HttpHeaders({
        // 'Content-Type': undefined,
        'Accept': '*/*',
        'Authorization': 
        "Bearer "+(JSON.parse(sessionStorage.getItem('token')).token),
        'Access-Control-Allow-Origin': this.apiURL,
        'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, PUT, PATCH, DELETE',
        'Access-Control-Allow-Headers': 'origin,X-Requested-With,content-type,accept',
        'Access-Control-Allow-Credentials': 'true' 

      })

3
没有其他解决方案适用于我。添加了 Accept: / 头之后,一切都像魔法般地正常工作了。 - iVoidWarranties
这就是问题所在 - 对于 Angular 15,如果省略 Content-Type,它会在 Angular 的其他地方设置为默认的 undefined,然而,明确地只提供 Accept 头部会排除 Content-Type 头部,现在我可以自由地进行操作了!太棒了,问题解决了! - undefined

15
fetch(url,options)
  1. 如果您将一个字符串设置为options.body,则必须在请求头中设置Content-Type,否则默认为text/plain
  2. 如果 options.body 是一个特定的对象,如 let a = new FormData()let b = new URLSearchParams(),则无需手动设置Content-Type。它会被自动添加。

    • 对于a,它会像这样:

    multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

    因此,边界是自动添加的。

    • 对于 b,它是 application/x-www-form-urlencoded;

非常感谢您告诉我们boundary会自动添加。我们都已经知道这个了。 - Green
你的解释更加清晰易懂,谢谢!我成功让我的代码运行了! - Eugenijus S.

9
根据FormData文档,在使用XMLHttpRequest或Fetch_API以multipart/form-data格式(例如上传文件和Blob对象)提交POST请求时,不应手动设置Content-Type头部字段,而是由浏览器自动设置。以下是该文档的警告提示:

警告:使用FormData对象以multipart/form-data格式提交POST请求时,请勿在请求中显式设置Content-Type头部字段。这样做将会阻止浏览器能够使用分界字符串来定界请求体中的表单字段。

所以,如果您的代码(或库/中间件等)手动设置了Content-Type头部字段,有两种方法可以解决:

  1. 重写代码(或其他使用方式),使其默认情况下不设置Content-Type头部字段
  2. 将Content-Type头部字段设置为undefined或从headers中删除它,让浏览器完成它的工作

6

添加headers:{content-type: undefined}浏览器将会为您生成一个边界,用于按部分和流方式上传文件。如果您添加'multiple/form-data',那么意味着您需要创建流并按部分上传文件。

因此,添加request.headers = {content-type: undefined}是可以的。


4

我正在使用aurelia-api(一个aurelia-fetch-client的包装器)。 在这种情况下,Content-Type默认为'application/json'。因此,我将Content-Type设置为未定义,它就像魔法般地工作了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接