如何使用 JS fetch API 上传文件?

IT技术 javascript fetch-api
2021-02-03 13:06:21

我仍然试图绕过它。

我可以让用户使用文件输入选择文件(甚至多个):

<form>
  <div>
    <label>Select file to upload</label>
    <input type="file">
  </div>
  <button type="submit">Convert</button>
</form>

我可以submit使用<fill in your event handler here>. 但是一旦我这样做了,我如何使用 发送文件fetch

fetch('/files', {
  method: 'post',
  // what goes here? What is the "body" for this? content-type header?
}).then(/* whatever */);
6个回答

我是这样做的:

var input = document.querySelector('input[type="file"]')

var data = new FormData()
data.append('file', input.files[0])
data.append('user', 'hubot')

fetch('/avatars', {
  method: 'POST',
  body: data
})
FormData如果您上传的只是文件(这是原始问题想要的),则不需要将文件内容包装在对象中。 fetch将接受input.files[0]上述作为其body参数。
2021-03-14 13:06:21
如果您有处理文件上传的 PHP 后端,您需要将文件包装在 FormData 中,以便正确填充 $_FILES 数组。
2021-03-25 13:06:21
/头像是什么意思?您指的是某个后端 API 端点吗?
2021-03-27 13:06:21
我还注意到谷歌浏览器由于某种原因不会在没有 FormData 部分的请求有效负载中显示文件。似乎是 Google Chrome 网络面板中的一个错误。
2021-04-07 13:06:21
这应该是正确的答案。另一种方式也有效,但更复杂
2021-04-08 13:06:21

这是一个带有注释的基本示例。upload功能是您正在寻找功能:

// Select your input type file and store it in a variable
const input = document.getElementById('fileinput');

// This will upload the file after having read it
const upload = (file) => {
  fetch('http://www.example.net', { // Your POST endpoint
    method: 'POST',
    headers: {
      // Content-Type may need to be completely **omitted**
      // or you may need something
      "Content-Type": "You will perhaps need to define a content-type here"
    },
    body: file // This is your file object
  }).then(
    response => response.json() // if the response is a JSON object
  ).then(
    success => console.log(success) // Handle the success response object
  ).catch(
    error => console.log(error) // Handle the error response object
  );
};

// Event handler executed when a file is selected
const onSelectFile = () => upload(input.files[0]);

// Add a listener on your input
// It will be triggered when a file will be selected
input.addEventListener('change', onSelectFile, false);
fileinput 是您点击上传的按钮的 id 吗?
2021-03-11 13:06:21
其他人只在服务器端得到一个空对象?
2021-03-14 13:06:21
为什么此示例包含 Content-Type 标头,但另一个答案说在使用 Fetch API 发送文件时省略它们?哪一个?
2021-03-15 13:06:21
您将如何从 Express 后端读取此文件。因为文件不是作为表单数据发送的。它只是作为文件对象发送。express-fileupload 或 multer 是否解析此类有效负载?
2021-03-27 13:06:21
不要设置内容类型。我花了很多时间试图让它工作,然后发现这篇文章说不要设置它。它有效!muffinman.io/uploading-files-using-fetch-multipart-form-data
2021-03-31 13:06:21

使用 Fetch API 发送文件的重要说明

需要省略 content-typeFetch 请求的标头。然后浏览器会自动添加Content type包括表单边界标题,它看起来像

Content-Type: multipart/form-data; boundary=—-WebKitFormBoundaryfgtsKTYLsT7PNUVD

表单边界是表单数据的分隔符

这是非常重要的信息,在MDN Fetch 文档中没有记录
2021-03-14 13:06:21
拒绝投票,因为即使它是有用的信息,但这并不试图回答 OP 的问题。
2021-03-19 13:06:21
这!很重要!不要在多部分上使用您自己的内容类型和 fetch。我不知道为什么我的代码不起作用。
2021-03-30 13:06:21
这是黄金!我浪费了 1 小时不理解这一点。感谢分享这个技巧
2021-04-06 13:06:21

如果你想要多个文件,你可以使用这个

var input = document.querySelector('input[type="file"]')

var data = new FormData()
for (const file of input.files) {
  data.append('files',file,file.name)
}

fetch('/avatars', {
  method: 'POST',
  body: data
})
请注意,file.name不需要显式传递文件名;根据 上的MDN 文档FormData.appendFile对象的默认名称已经是文件的文件名。
2021-03-27 13:06:21

要提交单个文件,您可以直接使用's数组中File对象作为初始值设定项的值input.filesbody:fetch()

const myInput = document.getElementById('my-input');

// Later, perhaps in a form 'submit' handler or the input's 'change' handler:
fetch('https://example.com/some_endpoint', {
  method: 'POST',
  body: myInput.files[0],
});

这是有效的,因为File继承自Blob,并且BlobBodyInitFetch 标准中定义的允许类型之一。

没有为我工作。express-fileupload未能解析请求流。FormData就像一个魅力。
2021-03-12 13:06:21
希望通过这个解决方案,浏览器将足够明智地流式传输文件,而不需要将其读入内存,@bhantol,但我并没有特意去寻找(无论是凭经验还是通过深入研究)规范)。如果您想确认,您可以尝试(在每个主要浏览器中)使用这种方法上传 50GB 文件或其他内容,然后查看您的浏览器是否尝试使用过多内存并被杀死。
2021-03-17 13:06:21
@attacomsian 乍一看,它在我看来就像express-fileupload是一个用于处理multipart/form-data包含文件的请求的服务器端库,所以是的,它与这种方法不兼容(它只是直接将文件作为请求正文发送)。
2021-03-26 13:06:21
这是最简单的答案,但如何body: myInput.files[0]导致客户端内存中保存的字节数?
2021-04-01 13:06:21