1.简介
在Html5中提供了一种通过File API规范与本地文件进行交互的标准方法。在使用 File API 在向服务器发送图片的过程中可以创建图片的缩略图预览,也可以允许应用程序在用户离线时保存文件引用。另外,您可以使用客户端逻辑来验证上传内容的 type 与其文件扩展名是否匹配,或者限制上传内容的大小。
该规范通过“本地”文件系统提供了多种文件访问接口:
1.1 FileList 对象
filelist对象是针对表单的file控件,当用户利用file控件选取文件以后,这个控件的files属性就是filelist对象。
//使用多选控件
<input type='file' id="file-test" multiple />
<script>
document.getElementById('file-test').onchange = function() {
console.log(this.files);
};
</script>
注:除了file控件以外,采用拖放的方式,也可以得到filelist对象。一般情况下,filelist对象只能被动的读取,不可以手动构造,只有在user主动触发(选取或拖拽)了文件读取的行为,javascript才会访问到filelist。
1.2 File对象
在filelist对象中就包括了file对象,file对象含有以下的属性:
name: 文件名,只读属性;
size: 文件大小,单位为字节,只读属性;
type: 文件的MIME类型,若是分辨不出类型,则为空字符串,只读属性;
lastModified:文件的上次修改时间,格式为时间戳;
lastModifiedDate:文件上次修改时间,格式为Date对象实例。
如下图所示:
1.3 Blob对象
Blob(Binary Large Object)对象代表了一段二进制数据,提供了一系列操作接口,其他二进制数据的API(比如File对象),都是建立在Blob对象基础上的,继承了它的属性和方法。
生成Blob对象有两种方法:一种是使用Blob构造函数,另一中是对现有的Blob对象使用slice方法切出一部分。
1.4 FileReader
FileReader对象用于读取文件,他的参数是file对象或blob对象。对于不同类型的文件,FileReader提供了不同的读取方法:
- readerAsText(blob|File,opt_encoding) :
返回文本字符串,默认情况下,文本编码格式是UTF-8,可以通过可选参数指定其他编码格式的文本。 - readerAsDataURL(Blob|File) :
返回一个基于Base64编码的data-uri对象(可用于<img>标签中的src属性,从而达到图片预览的效果)。 - readerAsArrayBuffer(Blob|File) :
返回一个ArrayBuffer对象。
FileReader对象采用异步方式读取文件,可以为一系列的事件指定回调函数。
- onabort() 方法 :读取中段或者调用reader.abort()方法时触发。
- onerror() 方法 :读取出错时触发。
- onload() 方法 :读取成功后触发。
- onloadend() 方法 :读取完成后触发,不管是否成功。触发顺序排在onload或onerror后面。
- onloadstart() 方法 :读取将要开始时触发。
- onprogress() 方法 :读取过程中周期性触发(一般可用于获取文件的读取进度)。
1.5 createObjectURL 方法
调用URL对象的createObjectURL方法,传入一个File对象或者Blob对象,能生成一个链接。这个URL可以放置于任何通常可以放置URL的地方,比如<img>标签的src属性。需注意的是即使是同样的二进制数据,每调用一次URL.createObjectURL方法。就会得到一个不一样的URL。
var URL = window.URL || window.webkitURL || window.mozURL ;
var SRC = URL.createObjectURL(file);
1.6 onprogress 事件
想要上传图片的时候显示进度条,在HTML5中提供了onprogress事件。onprogress事件可以作用于<video>或<audio>标签中,作为其状态回调函数的作用。也可以作为XMLHttpRequest的指定事件,XMLHttpRequest对象在传递数据的时候,提供了一个progress事件,用于返回进度信息。它分为 上传 和 下载 两种情况:
- 下载的 progress 事件属于 XMLHttpRequest 对象
- 上传的 progress 事件属于 XMLHttpRequest.upload 对象
当上传或者下载时,会频繁调用该方法。该方法接受一个事件参数event,其中event.total是需要传输的总字节,event.loaded是已经传输的字节。如果event.lengthComputable 不为真,则event.total等于0。
示例代码:
<body>
<input type="file" id="file">
<div class="progress">
<div></div>
</div>
<button onclick="ajaxUpload()">上传</button>
<script>
function ajaxUpload() {
var file = $('#file').get(0).files[0];
var formdata = new FormData();
formdata.append('file', file);
$.ajax({
url: 'test_progress.php',
type: 'post',
dataType: 'json',
data: formdata,//这里上传的数据使用了formData 对象
processData: false, //必须false才会自动加上正确的Content-Type
contentType: false, //必须设置
//这里我们先拿到jQuery产生的 XMLHttpRequest对象,为其增加 progress 事件绑定,然后再返回交给ajax使用.
xhr: () => {
var xhr = new XMLHttpRequest();
xhr.upload.onprogress = (evt) => {
console.log(evt);
var progressWidth = (evt.loaded / evt.total) * 100 + '%';
$('.progress > div').css('width', progressWidth);
}
return xhr;
}
})
}
</script>
</body>
2.检查环境
在编写前请先检查您当前的浏览器是否支持File API:
if(window.File &&window.FileReader &&window.FileList &&window.Blob) {
alert( "Success! The File APIs are fully supported in this browser")
}else{
alert('The File APIs are not fully supported in this browser.');
}
3.选取文件
3.1 通过表单输入进行选取文件
选取文件最常用的方法就死使用标准的<input type="file">元素,JS会返回选定的File对象的列表,下面的示例是使用“multiple”属性实现同时选定多个文件:
<div style="margin:0 auto;width:400px;height:200px;border:2px dashed pink">
<input type="file" id="filetest" name="filetest" multiple />
<output id="filelist"></output>
</div>
<script>
function handleFileSelect(event){
var files = event.target.files;
var output = [];
var i,f;
for(i=0;f=files[i];i++){
output.push('<li><strong>',f.name,'</strong>','-',f.size, 'bytes,last modified:',f.lastModifiedDate,'</li>');
}
document.getElementById("filelist").innerHTML = '<ul>'+output.join('')+'</ul>';
}
document.getElementById("filetest").addEventListener('change',handleFileSelect,false);
</script>
3.2 通过拖拽选取文件并显示缩略图
该方法是在本地将文件从桌面拖放到浏览器进行选取,并显示图片的缩略图,具体示例如下:
<style>
.thumb {
height: 80px;
border: 1px solid #eee;
margin: 10px 5px 0 0;
}
</style>
<body>
<div style="width: 320px;margin: 0 auto;border:2px solid skyblue;padding:10px;">
<div id="drop_zone" style="border:2px dashed pink;color:#bbb;text-align: center;line-height:76px;">Drop files here</div>
<output id="list"></output>
</div>
<script>
$(document).ready(function() {
function handleFileSelect(evt) {
evt.stopPropagation();
evt.preventDefault();
var files = evt.dataTransfer.files; // 拖拽选取时,FileList 对象格式.
var i,f;
for (i = 0; f = files[i]; i++) {
// 匹配图片格式
if (!f.type.match('image.*')) {
continue;
}
var reader = new FileReader(); //读取文件信息
// 捕获文件信息的闭包
reader.onload = (function(theFile) {
return function(e) {
// 显示缩略图
var span = document.createElement('span');
span.innerHTML = ['<img class="thumb" src="', e.target.result,
'" title="', escape(theFile.name), '"/>'].join('');
document.getElementById('list').insertBefore(span, null);
};
})(f);
// 以URL的形式读入图像文件。
reader.readAsDataURL(f);
}
}
function handleDragOver(evt) {
evt.stopPropagation();
evt.preventDefault();
evt.dataTransfer.dropEffect = 'copy';
}
// 设置dnd时间侦听器。
var dropZone = document.getElementById('drop_zone');
dropZone.addEventListener('dragover', handleDragOver, false);
dropZone.addEventListener('drop', handleFileSelect, false);
})
</script>
示例显示结果如下:
3.2 监听上传进度
在HTML5中,提供了一个FormData对象,能关于模拟一个原始的表单格式的数据,在之前的表单数据传输中,必须要用form标签将要传输的数据包裹起来了,按照规定的格式与后台传输,HTML5中的FormData对象就模拟这种格式,将form表单元素的value值与对应的name属性结合起来,组成一个querystring字符串。下面示例结合FormData实现通过onprogress事件监控读取状态,并以进度条的形式显示出来。
示例代码如下:
<div style="text-align: center; padding-top: 50px">
<input type="file" id="avatarfile" accept="image/png, image/jpeg, image/jpg, video/*"/>
<button onclick="to_upload_file()">上传文件</button>
<br>
<div style="text-align: left;margin-top: 15px;display: inline-block;width: 300px; height: 20px; border: 1px solid #44A1F8; border-radius: 2px;position: relative">
<div id="progress_bar" style="display: inline-block; width: 0px; height: 20px;background-color: #64B587"></div>
<div style="text-align: center;width: 300px;position: absolute; top: 0; font-size:16px; color: #413F43">
<div id="loading-percent">
上传进度0%
</div>
</div>
</div>
</div>
<script>
function to_upload_file(){
var fileObj = document.getElementById("avatarfile").files[0]
if(fileObj){
var formContent = new FormData();
formContent.append("file", fileObj);
var xhr = new XMLHttpRequest();
xhr.upload.onprogress = (e)=>{
var progress_bar = document.getElementById("progress_bar");
var loadingPercent = document.getElementById("loading-percent");
if(e.lengthComputable){
var loading = Math.round(e.loaded / e.total * 100);
}
if(loading === 100){
loadingPercent.innerHTML = "上传成功^_^";
}else{
loadingPercent.innerHTML = "上传进度"+loading+"%"
}
progress_bar.style.width = String(loading * 3) + "px";
}; // 监听上传进度
xhr.upload.onload = (e)=>{console.log("Success !", e)}; // 上传成功后的回调函数
xhr.upload.onerror = (e)=>{console.log("Failed", e)}; // 上传失败后的回调函数
xhr.open("POST", "http://127.0.0.1/upload_test.php", true);
xhr.send(formContent);
}else{
alert("当前无上传文件,请先选择文件后再上传")
}
}
</script>
参考文件:
https://www.jianshu.com/p/b3e986fb1237
http://www.w3.org/TR/file-upload