JavaScript:上传文件

305

假设我在页面上有以下元素:

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

这将创建一个按钮,允许网页的用户通过浏览器中的操作系统“文件打开…”对话框选择文件。

假设用户点击该按钮,在对话框中选择了一个文件,然后点击“确定”按钮关闭对话框。

现在所选的文件名已存储在:

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

现在,假设服务器在URL "/upload/image"处处理多部分POST请求。

我该如何将文件发送到"/upload/image"?

另外,我该如何监听通知以确保文件上传已完成?


2
JavaScript无法处理上传,因为它是服务器端的。服务器端脚本将接收文件,然后将其移动到所需文件夹中的php临时文件夹。 - Bakudan
我在这里找到了一个不错的PHP解决方案:http://www.w3schools.com/php/php_file_upload.asp - mld
27
因为这个问题是关于普通的JavaScript,而不是jQuery,所以我投票重新开放 - Déjà vu
4个回答

238

纯JavaScript

你可以使用 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://dev59.com/W1wX5IYBdhLWcg3w0SNf#33355142">not throwing</a> at all but we can read response status which contains code)

传统方法 - 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://dev59.com/nm445IYBdhLWcg3wKHEJ#10173639">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>)

概要

  • 在服务器端,您可以通过请求的filename formData参数自动读取原始文件名(和其他信息)。
  • 您不需要设置请求头Content-Typemultipart/form-data - 浏览器将自动设置它,并包括强制性的boundary参数
  • 除了/upload/image,您还可以使用完整的地址,如http://.../upload/image。同时,参数method也是任意的,通常在服务器上用于文件上传的是“POST”,但有时可能会用到“PUT”或其他方法。
  • 如果您想在单个请求中发送多个文件,请使用multiple属性:<input multiple type=... />,并以类似的方式连接所有选择的文件 (e.g. photo2=...files[2];... formData.append("photo2", photo2);)
  • 您可以通过以下方式向请求中添加附加数据(json),例如:let user = {name:'john', age:34}formData.append("user", JSON.stringify(user));
  • 您可以使用AbortControllerfetch设置超时,对于旧的方法,可以通过xhr.timeout = milisec进行设置。
  • 这些解决方案应该适用于所有主要的浏览器。

1
我遇到了错误“net::ERR_ABORTED 405 (Method Not Allowed)” - Hidayt Rahman
1
@HidaytRahman 当你的URL中的服务器未实现POST方法时,通常会出现此错误。 - Kamil Kiełczewski
1
奇怪 - 根据你的标题,我认为我们不再需要服务器:D - Hidayt Rahman
我得到了POST http://localhost:8000/upload/image 404 (未找到)的错误。在我的React Node测试项目的/src和/public目录下创建了/upload/image。 - Jay J
@KamilKiełczewski 谢谢。即使我在获取中使用“/”,它的行为也是相同的。这是一个Node实现。您有任何指针可以配置的地方吗?http://localhost:8000 可以正确提供主页,因此URL有效。 - Jay J
显示剩余5条评论

107

或使用其src =“”内部的图像上传器脚本的iframe 0x0大小,并将表单发布到其中,使用target =“iframeId”在iframe src上读取结果。 .load事件,例如可以使用jQuery绑定。 - Dmitry
15
现如今,通过Javascript和Ajax进行点赞是完全可行的,参考链接:https://dev59.com/1nE95IYBdhLWcg3watM3#10811427(我没有给你的回答点“踩”)。 - KajMagnus
46
@KajMagnus,你可能是想说“上传”(upload)但不小心说成了“点赞”(upvote)。 - Samuel Allan
我尝试了这种方法,但是“request”选项似乎没有包含服务器中的图像文件。 - zean_7

6

我已经尝试了一段时间,但是这些答案都不能起作用。以下是我的解决方法。

我有一个选择文件和提交按钮。

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

然后在我的 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();
};

如果您喜欢 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>

PUT方法与POST方法略有不同。在这种情况下,使用chrome的web服务器时,POST方法未实现。
通过Web Server for Chrome测试 - https://chrome.google.com/webstore/detail/web-server-for-chrome/ofhbbkphhbklhfoeikjpcbhemlocgigb?hl=zh-CN 注意 - 使用Web Server for Chrome时,您需要进入高级选项并检查“启用文件上传”选项。如果没有这样做,则会因为未允许而出现错误。

3

以下是关于提交包含非表单文件的表单的答案:

const formData = new FormData();
const files = event.target.files;

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

$.ajax({
   type: "POST",
   url: urlString,
   data: formData,
   error : function (result) {
       console.log('error', result);
   },
   success : function (result) {
      console.log('success', result)
   }
});

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