HTML5提供了很多有用的API,其中就包括上传的API,XMLHttpRequest2.0,在HTML5时代之前,需要进行二进制的上传一般都会才用flash的方案,但是当XMLHttpRequest2.0出来之后,完全可以使用HTML5的上传解决方案,能够非常方便的进行二进制上传进度的显示,上传图片的本地预览,甚至可以做到断点续传,分片上传,多文件上传等各种复杂的底层功能。
首先回顾一下XMLHttpRequest1.0的传输过程
关于XMLHttpRequest
初始化XMLHttpRequest
想要使用XMLHttpRequest进行传输文件,首先我们得创建一个XMLHttpRequest对象, 而每创建一个XMLHttpRequest的时候会产生readyState这个属性,当一个 XMLHttpRequest 初次创建时,这个属性的值从 0 开始,直到接收到完整的 HTTP 响应,这个值增加到 4。 5 个状态中每一个都有一个相关联的非正式的名称,下表列出了状态、名称和含义:
状态 | 名称 | 描述 |
0 | Uninitialized | 初始化状态。XMLHttpRequest 对象已创建或已被 abort() 方法重置。 |
1 | Open | open() 方法已调用,但是 send() 方法未调用。请求还没有被发送。 |
2 | Sent | Send() 方法已调用,HTTP 请求已发送到 Web 服务器。未接收到响应。 |
3 | Receiving | 所有响应头部都已经接收到。响应体开始接收但未完成。 |
4 | Loaded | HTTP 响应已经完全接收。 |
var xhr = new XMLHttpRequest(); //readyState为0
xhr.open方法设置初始参数
然后需要初始化一些HTTP请求的参数但是这里只是初始化,也就是说设置一些上传所需要的参数,但是并不会进行上传
xhr.open(method, url, async, username, password); //readyState为1
method 参数是用于请求的 HTTP 方法。值包括 GET、POST 和 HEAD。
url 参数是请求的主体。大多数浏览器实施了一个同源安全策略,并且要求这个 URL 与包含脚本的文本具有相同的主机名和端口。
async 参数指示请求使用应该异步地执行。如果这个参数是 false,请求是同步的,后续对 send() 的调用将阻塞,直到响应完全接收。如果这个参数是 true 或省略,请求是异步的,且通常需要一个 onreadystatechange 事件句柄。
username 和 password 参数是可选的,为 url 所需的授权提供认证资格。如果指定了,它们会覆盖 url 自己指定的任何资格。
xhr.send()方法发送请求
对HTTP的请求的参数设置完之后就可以进行发送了
xhr.send(); //readyState为2
xhr.send()会导致一个HTTP请求。如果之前没有调用 open()或者说 readyState 不是 1,xhr.send() 则会跑出一个错误。否则,它发送一个 HTTP 请求,该请求由以下几部分组成: 之前调用xhr.open() 时指定的 HTTP 方法、URL 以及认证资格(如果有的话)。 之前调用 xhr.setRequestHeader() 时指定的请求头部(如果有的话)。 传递给这个方法的 body 参数。
请求发布后,send() 把 readyState 设置为 2,并触发 onreadystatechange 事件。
关于 onreadystatechange 事件
onreadystatechange实际上是每当readyState发生改变的时候就会触发的参数,也就是说我们可以直接在onreadystatechange事件中去判断readyState的值,假如readyState等于4的时候就可以执行上传完成后的回调方法。
如果之前调用的xhr.open() 参数 async 为 false,这个方法会阻塞并不会返回,直到 readyState 为 4 并且服务器的响应被完全接收。否则,如果 async 参数为 true,或者这个参数省略了,xhr.send() 立即返回。如果服务器响应带有一个 HTTP 重定向,xhr.send() 方法或后台线程自动遵从重定向。当所有的 HTTP 响应头部已经接收,xhr.send() 或后台线程把 readyState 设置为 3 并触发 onreadystatechange 事件句柄。如果响应较长,xhr.send() 或后台线程可能在状态 3 中触发 onreadystatechange 事件句柄:这可以作为一个下载进度指示器。最后,当响应完成,xhr.send() 或后台线程把 readyState 设置为 4,并最后一次触发事件句柄。
通过上面的初始化,open,send,已经监控onreadystatechange事件的返回值用以触发回调方法,我们就完成了一个ajax请求。
关于XMLHttpRequest2.0
而关于HTML5中增加XMLHttpRequest2.0的新功能主要包括下面这些:
- 可以设置HTTP请求的时限。
- 可以使用FormData对象管理表单数据。
- 可以上传文件。
- 可以请求不同域名下的数据(跨域请求)。
- 可以获取服务器端的二进制数据。
- 可以获得数据传输的进度信息。
我们这里主要说和上传文件比较密切相关的部分
关于FormData
AJAX通常可以用来进行模拟表单的提交,也就是说XMLHttpRequest1.0中大家经常使用的无刷新提交数据,为了方便对于表单的处理,HTML新增了一个FormData对象,可以用来模拟表单。使用如下:
var formData=new FormData(); formData.append('name',"Jack"); formData.append('uid',666666);
上面的三行代码就实现了往一个FormData()对象里面插入了两个字段,一个name,一个uid 然后我们可以就像XMLHttpRequest1.0的时候一样进行ajax的提交即:
var xhr=new XMLHttpRequest(); xhr.open("post",url); xhr.send(formData);
关于HTML5多文件的上传
那我们实际上想要用HTML5解决上传文件的过程应该怎么解决呢? HTML5中针对<input type="file"/>
标签新加了一个files的对象,并且通过对input标签设置multiple属性,则可以实现添加多个文件的功能,而files实际上是一个数组对象,里面存的则全是这个file标签中的文件;
那么我们想要上传文件就很简单了,直接循环files对象获取到用户添加到file控件中的文件并且都通过FormData对象的append方法添加到FormData中去,然后进行发送即可。
var formData=new FormData(); for(var i=0;i<files.length;i++){ formData.append(i,files[i]); } var xhr=new XMLHttpRequest(); xhr.open("post",url); xhr.send(formData);
通过上面这些代码就可以实现对于多文件的上传了。
关于HTML5上传中的各种需求
虽然上面实现了对于多个文件的上传,但是大家在实际工作中对于上传的需求肯定不单单是能够上传而已,下面我们就来说下利用HTML5对于上传API中新增加的一大堆好用的东西所能够实现的效果;
上传之前的本地预览
大家在做文件上传之前,经常会碰到需要预览文件大小,文件名称等信息,关于这些HTML5已经帮我们封装好了相应的API,我们只需要调用方法即可 上面说到关于<input type="file" />
标签中有一个files数组对象,实际上这个数组对象中的每一个值都有一些相应的属性可以调用
- name – 文件名(不包含路径)
- type – 文件的MIME类型(小写)
- size – 文件的尺寸(单位为字节)
通过调用上面的三个属性就可以获得每一个文件在本地的一些相关信息,相关方法如下
function fileSelected() { var file = document.getElementById('fileToUpload').files[0]; //获取到上传控件对象files if (file) { var fileSize = 0; if (file.size > 1024 * 1024){ fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB'; }else{ fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB'; } document.getElementById('fileName').innerHTML = 'Name: ' + file.name; document.getElementById('fileSize').innerHTML = 'Size: ' + fileSize; document.getElementById('fileType').innerHTML = 'Type: ' + file.type; } }
关于FileReader
关于本地的预览,一些文件的相关信息我们都已经可以拿到了,但是假如我想要上传的是一张图片,想要直接本地预览这张图片能不能直接在本地完成呢?答案是可以的,只需要你掌握FileReader这个对象即可
FileReader对象一共有4个方法,其中3个可以读取文件,另一个用来中断读取。无论读取成功或失败,方法并不会返回读取结果,这一结果存储在result属性中。 4个方法分别是:
- readAsBinaryString(file) 将文件读取为二进制编码
- readAsText(file,[encoding]) 将文件读取为文本
- readAsDataURL(file) 将文件读取为DataURL
- abort 中断读取操作
FileReader对象同样也有很多事件,分别是:
- onabort 中断
- onerror 出错
- onloadstart 开始
- onprogress 正在读取
- onload 成功读取
- onloadend 读取完成,无论成功失败
那我们在上面的事件方法基础上就可以实现关于本地预览图片的相关方法:
var reader = new FileReader(); //创建一个FileReader对象 reader.readAsDataURL(file); //将文件以Data URL形式读入页面 ,并且将结果存在result属性中 reader.οnlοad=function(e){ //当文件成功读取后 var imgBox=document.getElementById("imgBox"); imgBox.innerHTML='<img src="' + this.result +'" alt="" />'; //显示文件 }
通过上面的方法就可以实现图片的本地预览了,当然上面那个方法还可以结合之前的files对象中的type,size等属性实现上传图片类型大小的限制。
关于HTML5的上传部分,实际上还有很多应用的地方,将会在以后的博客中告诉大家。