使用axios发送multipart/form-data POST请求时如何同时发送文件和JSON数据

60

我正在尝试将文件和一些JSON数据一起发送到我的REST端点,使用axios库直接从JavaScript发出请求,如下所示的方法。

doAjaxPost() {
    var formData = new FormData();
    var file = document.querySelector('#file');

    formData.append("file", file.files[0]);
    formData.append("document", documentJson);

    axios({
        method: 'post',
        url: 'http://192.168.1.69:8080/api/files',
        data: formData,
    })
    .then(function (response) {
        console.log(response);
    })
    .catch(function (response) {
        console.log(response);
    });
}

然而,问题在于当我在Chrome开发者工具中检查网络选项卡中的请求时,我发现对于文档没有Content-Type字段,而对于文件字段Content-Typeapplication/pdf(我正在发送一个PDF文件)。

Request shown in network inspector

在服务器上,文档的Content-Typetext/plain;charset=us-ascii
更新:
我通过Postman成功发送了一个正确的请求,将document作为.json文件发送。尽管我发现这只在Linux/Mac上有效。
4个回答

106

要设置内容类型,您需要传递类似文件的对象。您可以使用 Blob 创建一个这样的对象。

const obj = {
  hello: "world"
};
const json = JSON.stringify(obj);
const blob = new Blob([json], {
  type: 'application/json'
});
const data = new FormData();
data.append("document", blob);
axios({
  method: 'post',
  url: '/sample',
  data: data,
})

1
我找了很久都没有找到解决方案,而你几乎瞬间就给出了正确的答案。非常感谢你! :) - pavlee
7
你在这里将文件添加到哪里?这段代码示例中是否缺少它? - ElectRocnic
1
@ElectRocnic - 整个答案都是关于在内存中生成JSON文件并追加的。我没有重复从文件输入读取文件的逻辑,因为问题不是关于那个的(而且演示该逻辑的代码已经在问题中了)。 - Quentin
8
谢谢,我已经把它跑起来了。唯一缺失的一行是 formData.append("file", file), 我已经加上去了,现在可以正常工作了 :) - ElectRocnic
1
非常有用。谢谢。在服务器上,我该如何解压缩“document”?我看到 blob 也被上传了,但我不想上传 blob,只需要解压缩“document”。我该怎么做? - abhisek
显示剩余6条评论

15

试试这个。

doAjaxPost() {
    var formData = new FormData();
    var file = document.querySelector('#file');

    formData.append("file", file.files[0]);
    // formData.append("document", documentJson); instead of this, use the line below.
    formData.append("document", JSON.stringify(documentJson));

    axios({
        method: 'post',
        url: 'http://192.168.1.69:8080/api/files',
        data: formData,
    })
    .then(function (response) {
        console.log(response);
    })
    .catch(function (response) {
        console.log(response);
    });
}

你可以在后端解码这个字符串化的JSON。


1
但是后端接收到的 blob 是对象的字符串,例如 '[object Object]'。 - Fullstack Engineer

4
你不能将内容类型设置为documentJson,因为非文件字段不得具有Content-Type标头,请参见HTML 5规范4.10.21.8多部分表单数据

实现你的目标有两种方法:

  • 使用JSON.stringify对数据进行编码,并在后端进行解码,就像下面的答案一样
  • 传递类似文件的对象并设置Content-Type,就像下面的答案一样

五年后...开玩笑的,实际上是个很好的答案。 - pavlee

2

你只需要在请求中添加正确的头部信息即可。

axios({
  method: 'post',
  url: 'http://192.168.1.69:8080/api/files',
  data: formData,
  header: {
            'Accept': 'application/json',
            'Content-Type': 'multipart/form-data',
          },
    })

我的标头字段已经正确设置。问题出在载荷中的 Content-Type 上。 - pavlee
是的,准确地说,在请求中写的是 text/plain,但应该是 multipart/form-data。 - eth3rnit3
8
这将覆盖多部分数据的内容类型,丢弃边界参数并破坏服务器解析它的能力。但这不会对该多部分数据中“文档”部分的内容类型产生任何影响。 - Quentin

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