简体   繁体   English

JavaScript:上传文件

[英]JavaScript: Upload file

Let's say I have this element on the page:假设我在页面上有这个元素:

<input id="image-file" type="file" />

This will create a button that allows the users of the web page to select a file via an OS "File open..." dialog in the browser.这将创建一个按钮,允许网页用户通过浏览器中的操作系统“文件打开...”对话框选择文件。

Let's say the user clicks said button, selects a file in the dialog, then clicks the "Ok" button to close the dialog.假设用户单击所述按钮,在对话框中选择一个文件,然后单击“确定”按钮关闭对话框。

The selected file name is now stored in:选定的文件名现在存储在:

document.getElementById("image-file").value

Now, let's say that the server handles multi-part POSTs at the URL "/upload/image".现在,假设服务器在 URL“/upload/image”处处理多部分 POST。

How do I send the file to "/upload/image"?如何将文件发送到“/upload/image”?

Also, how do I listen for notification that the file is finished uploading?另外,我如何收听文件上传完成的通知?

Pure JS纯JS

You can use fetch optionally with await-try-catch您可以选择将fetch与 await-try-catch 一起使用

let photo = document.getElementById("image-file").files[0];
let formData = new FormData();
     
formData.append("photo", photo);
fetch('/upload/image', {method: "POST", body: formData});

 async function SavePhoto(inp) { let user = { name:'john', age:34 }; let formData = new FormData(); let photo = inp.files[0]; formData.append("photo", photo); formData.append("user", JSON.stringify(user)); const ctrl = new AbortController() // timeout setTimeout(() => ctrl.abort(), 5000); try { let r = await fetch('/upload/image', {method: "POST", body: formData, signal: ctrl.signal}); console.log('HTTP response code:',r.status); } catch(e) { console.log('Huston we have problem...:', e); } }
 <input id="image-file" type="file" onchange="SavePhoto(this)" > <br><br> Before selecting the file open chrome console > network tab to see the request details. <br><br> <small>Because in this example we send request to https://stacksnippets.net/upload/image the response code will be 404 ofcourse...</small> <br><br> (in stack overflow snippets there is problem with error handling, however in <a href="https://jsfiddle.net/Lamik/b8ed5x3y/5/">jsfiddle version</a> for 404 errors 4xx/5xx are <a href="https://stackoverflow.com/a/33355142/860099">not throwing</a> at all but we can read response status which contains code)

Old school approach - xhr老派方法 - xhr

let photo = document.getElementById("image-file").files[0];  // file from input
let req = new XMLHttpRequest();
let formData = new FormData();

formData.append("photo", photo);                                
req.open("POST", '/upload/image');
req.send(formData);

 function SavePhoto(e) { let user = { name:'john', age:34 }; let xhr = new XMLHttpRequest(); let formData = new FormData(); let photo = e.files[0]; formData.append("user", JSON.stringify(user)); formData.append("photo", photo); xhr.onreadystatechange = state => { console.log(xhr.status); } // err handling xhr.timeout = 5000; xhr.open("POST", '/upload/image'); xhr.send(formData); }
 <input id="image-file" type="file" onchange="SavePhoto(this)" > <br><br> Choose file and open chrome console > network tab to see the request details. <br><br> <small>Because in this example we send request to https://stacksnippets.net/upload/image the response code will be 404 ofcourse...</small> <br><br> (the stack overflow snippets, has some problem with error handling - the xhr.status is zero (instead of 404) which is similar to situation when we run script from file on <a href="https://stackoverflow.com/a/10173639/860099">local disc</a> - so I provide also js fiddle version which shows proper http error code <a href="https://jsfiddle.net/Lamik/k6jtq3uh/2/">here</a>)

SUMMARY概括

  • In server side you can read original file name (and other info) which is automatically included to request by browser in filename formData parameter.在服务器端,您可以读取原始文件名(和其他信息),这些文件名自动包含在filename formData 参数中的浏览器请求中。
  • You do NOT need to set request header Content-Type to multipart/form-data - this will be set automatically by browser (which will include the mandatory boundary parameter ).您不需要将请求标头Content-Type设置为multipart/form-data - 这将由浏览器自动设置(其中将包括强制boundary参数)。
  • Instead of /upload/image you can use full address like http://.../upload/image (of course both addresses are arbitrary and depends on server - and same situation with param method - usually on servers "POST" is used for file upload but sometimes "PUT" or other can be used).而不是/upload/image你可以使用像http://.../upload/image这样的完整地址(当然这两个地址都是任意的,并且取决于服务器 - 与参数method相同的情况 - 通常在服务器上使用“POST”用于文件上传,但有时可以使用“PUT”或其他)。
  • If you want to send many files in single request use multiple attribute: <input multiple type=... /> , and attach all chosen files to formData in similar way (eg photo2=...files[2]; ... formData.append("photo2", photo2); )如果您想在单个请求中发送多个文件,请使用multiple属性: <input multiple type=... /> ,并以类似的方式将所有选择的文件附加到 formData(例如photo2=...files[2]; ... formData.append("photo2", photo2); )
  • You can include additional data (json) to request eg let user = {name:'john', age:34} in this way: formData.append("user", JSON.stringify(user));您可以包含附加数据(json)以请求例如let user = {name:'john', age:34}以这种方式: formData.append("user", JSON.stringify(user));
  • You can set timeout: for fetch using AbortController , for old approach by xhr.timeout= milisec您可以设置超时:对于使用AbortController fetch ,对于旧方法通过xhr.timeout= milisec
  • This solutions should work on all major browsers.此解决方案应适用于所有主要浏览器。

Unless you're trying to upload the file using ajax, just submit the form to /upload/image .除非您尝试使用 ajax 上传文件,否则只需将表单提交/upload/image

<form enctype="multipart/form-data" action="/upload/image" method="post">
    <input id="image-file" type="file" />
</form>

If you do want to upload the image in the background (eg without submitting the whole form), you can use ajax:如果您确实想在后台上传图片(例如,不提交整个表单),您可以使用 ajax:

As its creator I'm biased ;) but you could also consider using something like https://uppy.io . 作为其创建者,我有偏见;),但您也可以考虑使用https://uppy.io之类的东西。 It does file uploading without navigating away from the page and offers a few bonuses like drag & drop, resuming uploads in case of browser crashes/flaky networks, and importing from eg Instagram. 它可以在不离开页面的情况下进行文件上传,并提供一些额外的功能,例如拖放,在浏览器崩溃/网络不稳定的情况下恢复上传以及从例如Instagram导入。 It's also open source and does not rely on jQuery or anything like that. 它也是开源的,不依赖jQuery或类似的东西。

I have been trying to do this for a while and none of these answers worked for me.我一直在尝试这样做一段时间,但这些答案都不适合我。 This is how I did it.我就是这样做的。

I had a select file and a submit button我有一个选择文件和一个提交按钮

<input type="file" name="file" id="file">
<button onclick="doupload()" name="submit">Upload File</button>

Then in my javascript code I put this然后在我的javascript代码中我把这个

function doupload() {
    let data = document.getElementById("file").files[0];
    let entry = document.getElementById("file").files[0];
    console.log('doupload',entry,data)
    fetch('uploads/' + encodeURIComponent(entry.name), {method:'PUT',body:data});
    alert('your file has been uploaded');
    location.reload();
};

If you like StackSnippets...如果你喜欢 StackSnippets...

 function doupload() { let data = document.getElementById("file").files[0]; let entry = document.getElementById("file").files[0]; console.log('doupload',entry,data) fetch('uploads/' + encodeURIComponent(entry.name), {method:'PUT',body:data}); alert('your file has been uploaded'); };
 <input type="file" name="file" id="file"> <button onclick="doupload()" name="submit">Upload File</button>

The PUT method is slightly different than the POST method. PUT方法与POST方法略有不同。 In this case, in web server for chrome, the POST method is not implemented.在这种情况下,在 chrome 的 Web 服务器中, POST方法没有实现。

Tested with web server for chrome - https://chrome.google.com/webstore/detail/web-server-for-chrome/ofhbbkphhbklhfoeikjpcbhemlocgigb?hl=en使用 chrome 的网络服务器测试 - https://chrome.google.com/webstore/detail/web-server-for-chrome/ofhbbkphhbklhfoeikjpcbhemlocgigb?hl=en

Note- When using web server for chrome you need to go into advanced options and check the option 'enable file upload'.注意 - 使用 chrome 的 Web 服务器时,您需要进入高级选项并选中“启用文件上传”选项。 If you do not, you will get an error for not allowed.如果你不这样做,你会得到一个不允许的错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM