【问题标题】:How do I upload a file with the JS fetch API?如何使用 JS fetch API 上传文件?
【发布时间】:2017-06-01 10:06:00
【问题描述】:

我仍在努力解决它。

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

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

我可以使用&lt;fill in your event handler here&gt; 捕获submit 事件。但是一旦我这样做了,如何使用fetch 发送文件?

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

【问题讨论】:

标签: javascript fetch-api


【解决方案1】:

我是这样做的:

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 参数。
  • 如果您有一个 PHP 后端处理文件上传,您需要将文件包装在 FormData 中,以便正确填充 $_FILES 数组。
  • 我还注意到,由于某种原因,Google Chrome 不会在没有 FormData 部分的情况下在请求负载中显示文件。似乎是 Google Chrome 网络面板中的一个错误。
  • 这应该是正确的答案。另一种方式也有效,但更复杂
  • 您将如何从 Express 后端读取此文件。由于文件不作为表单数据发送。它只是作为文件对象发送。 express-fileupload 或 multer 是否会解析此类有效负载?
【解决方案2】:

这是一个使用 cmets 的基本示例。 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);

【讨论】:

  • 为什么此示例包含 Content-Type 标头,但另一个答案说在使用 Fetch API 发送文件时省略它们?是哪一个?
  • 不要设置内容类型。我花了很多时间试图让它工作,然后发现这篇文章说不要设置它。它有效! muffinman.io/uploading-files-using-fetch-multipart-form-data
  • 您将如何从 Express 后端读取此文件。由于文件不作为表单数据发送。它只是作为文件对象发送。 express-fileupload 或 multer 是否会解析此类有效负载?
  • fileinput是你点击上传按钮的id吗?
  • 还有其他人在服务器端只得到一个空对象吗?
【解决方案3】:

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

需要省略 Fetch 请求的 content-type 标头。然后浏览器会自动添加 Content type 标头,包括看起来像

的表单边界
Content-Type: multipart/form-data; boundary=—-WebKitFormBoundaryfgtsKTYLsT7PNUVD

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

【讨论】:

  • 这个!很重要!不要在多部分上使用您自己的内容类型和 fetch。我不知道为什么我的代码不起作用。
  • 这是金子!我浪费了 1 小时不理解这一点。感谢分享这个技巧
  • 投反对票,因为尽管它是有用的信息,但这并不试图回答 OP 的问题。
  • 这是非常重要的信息,在MDN Fetch docs 中没有捕获。
【解决方案4】:

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

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 docs on FormData.appendFile 对象的默认名称已经是文件的文件名。
【解决方案5】:

要提交单个文件,您可以直接使用input.files 数组中的File 对象作为body: 的值在您的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,而Blob 是Fetch Standard 中定义的允许BodyInit 类型之一。

【讨论】:

  • 这是最简单的答案,但body: myInput.files[0] 是如何影响客户端内存中保存的字节数的?
  • 我会期望使用这个解决方案,浏览器将足够明智地流式传输文件并且不需要将其读入内存,@bhantol,但我没有不遗余力地找出答案(无论是凭经验还是通过深入研究规范)。如果您想确认,您可以尝试(在每个主要浏览器中)使用这种方法上传一个 50GB 的文件或其他东西,看看您的浏览器是否尝试使用过多的内存并被杀死。
  • 对我不起作用。 express-fileupload 解析请求流失败。但是FormData 就像一个魅力。
  • @attacomsian 在我看来,express-fileupload 是一个服务器端库,用于处理包含文件的multipart/form-data 请求,所以是的,它与这种方法不兼容(它只是直接发送文件作为请求正文)。
【解决方案6】:

这里接受的答案有点过时了。截至 2020 年 4 月,在 MDN 网站上看到的推荐方法建议使用 FormData 并且也不要求设置内容类型。 https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

为了方便,我引用了代码 sn-p:

const formData = new FormData();
const fileField = document.querySelector('input[type="file"]');

formData.append('username', 'abc123');
formData.append('avatar', fileField.files[0]);

fetch('https://example.com/profile/avatar', {
  method: 'PUT',
  body: formData
})
.then((response) => response.json())
.then((result) => {
  console.log('Success:', result);
})
.catch((error) => {
  console.error('Error:', error);
});

【讨论】:

  • 使用FormData 仅在服务器需要表单数据时才有效。如果服务器想要一个原始文件作为 POST 的正文,则接受的答案是正确的。
  • @Clyde 你是什么意思?我以为是客户端决定向服务器发送什么类型的内容?
  • 是的,但是客户端和服务器必须就可以发送哪些内容以及如何编码达成一致。当然可以编写服务器代码来接受原始 POST 正文或 FormData(标头会说明客户端使用了什么编码),但通常服务器会期望特定的编码,因此您必须发送匹配的内容.
【解决方案7】:

从 Alex Montoya 的多个文件输入元素的方法中跳出来

const inputFiles = document.querySelectorAll('input[type="file"]');
const formData = new FormData();

for (const file of inputFiles) {
    formData.append(file.name, file.files[0]);
}

fetch(url, {
    method: 'POST',
    body: formData })

【讨论】:

    【解决方案8】:

    添加 php 端点示例会很好。 那就是js:

    const uploadinput = document.querySelector('#uploadinputid');
    const uploadBtn = document.querySelector('#uploadBtnid');
    uploadBtn.addEventListener('click',uploadFile);
    
    async function uploadFile(){
        const formData = new FormData();
        formData.append('nameusedinFormData',uploadinput.files[0]);    
        try{
            const response = await fetch('server.php',{
                method:'POST',
                body:formData
            } );
            const result = await response.json();
            console.log(result);
        }catch(e){
            console.log(e);
    
        }
    }
    

    那是php:

    $file = $_FILES['nameusedinFormData'];
    $temp = $file['tmp_name'];
    $target_file = './targetfilename.jpg';
    move_uploaded_file($_FILES["image"]["tmp_name"], $target_file);
    

    【讨论】:

    • 这个问题没有假设用于上传文件的后端技术。
    【解决方案9】:

    我的问题是我使用 response.blob() 来填充表单数据。显然你至少不能用 react native 来做到这一点,所以我最终使用了

    data.append('fileData', {
      uri : pickerResponse.uri,
      type: pickerResponse.type,
      name: pickerResponse.fileName
     });
    

    Fetch 似乎可以识别该格式并将文件发送到 uri 指向的位置。

    【讨论】:

      【解决方案10】:

      这是我的代码:

      html:

      const upload = (file) => {
          console.log(file);
      
          
      
          fetch('http://localhost:8080/files/uploadFile', { 
          method: 'POST',
          // headers: {
          //   //"Content-Disposition": "attachment; name='file'; filename='xml2.txt'",
          //   "Content-Type": "multipart/form-data; boundary=BbC04y " //"multipart/mixed;boundary=gc0p4Jq0M2Yt08jU534c0p" //  ή // multipart/form-data 
          // },
          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
        );
      
        //cvForm.submit();
      };
      
      const onSelectFile = () => upload(uploadCvInput.files[0]);
      
      uploadCvInput.addEventListener('change', onSelectFile, false);
      <form id="cv_form" style="display: none;"
      										enctype="multipart/form-data">
      										<input id="uploadCV" type="file" name="file"/>
      										<button type="submit" id="upload_btn">upload</button>
      </form>
      <ul class="dropdown-menu">
      <li class="nav-item"><a class="nav-link" href="#" id="upload">UPLOAD CV</a></li>
      <li class="nav-item"><a class="nav-link" href="#" id="download">DOWNLOAD CV</a></li>
      </ul>

      【讨论】:

      • 点评来源: 您好,请不要只用源代码回答。尝试对您的解决方案如何工作提供一个很好的描述。请参阅:How do I write a good answer?。谢谢
      【解决方案11】:

      如何使用 HTML5 fetch 上传单个文件

      <label role="button">
        Upload a picture
        <input accept="image/*" type="file" hidden />
      </label>
      
      const input = document.querySelector(`input[type="file"]`);
      
      function upload() {
        fetch(uplaodURL, { method: "PUT", body: input.files[0] });
      }
      
      input.addEventListener("change", upload); 
      

      【讨论】:

        猜你喜欢
        • 2020-06-22
        • 2021-03-03
        • 1970-01-01
        • 1970-01-01
        • 2021-11-06
        • 2018-07-30
        相关资源
        最近更新 更多