HTML5之fileReader异步读取文件及文件切片读取

  • fileReader的方法与事件
  • fileReade实现图片预加载
  • fileReade实现文件读取进度条
  • fileReade的与file.s实现文件切片读取

一、fileReader的方法与事件

1.方法

  • FileReader.abort():终止读取操作。返回时,readyState属性为DONE。
  • FileReader.readAsArrayBuffer():将文件读取为ArrayBuffer数据对象。
  • FileReader.readAsBinaryString():将文件读取为二进制数据。
  • FileReader.readAsDataURL():将文件读取为DataURL编码(base64)==>URL格式的字符串。
  • FileReader.readAsText():将文件读取为文本==》字符串表示的文件内容。

2.事件

  • FileReader.onloadstart:读取开始时触发
  • FileReader.onprogress:读取中
  • FileReader.onloadend:读取完成触发,无论成功或失败
  • FileReader.onload:文件读取成功完成时触发
  • FileReader.onabort:中断时触发
  • FileReader.onerror:出错时触发

3.实现图片读取预览

在Web FileReader API接口实现之前,图片预览的通常做法是先将图片上传至服务器,上传成功以后通过过触发ajax请求到刚刚上传的图片,然后加载到页面。这个过程中如果图片选择错误或者需要修改上传的图片,就需要重复上传和下载请求,并且还需要在服务器替换图片资源,会浪费大量的网络资源和服务器资源。现在通过FileReader实现本地图片读取预览,就可以在本地实现图片修改,节省服务器资源。

既然是HTML5的API就目前来说肯定存在兼容性问题,目前IE10开始支持FileReader,所以通过服务上传下载的图片预览方式还是有必要的,接下来的示例仅仅展示FileReader的图片读取预览代码:

 1 <style>
 2     .imgBox{
 3         display: flex;
 4         width: 300px;
 5         height: 300px;
 6         border: 1px solid #300;
 7         justify-content: center;
 8         align-items: center;
 9     }
10 </style>
11 <input type="file" name="">
12 <div class="imgBox"></div>
13 <script>
14     var imgBox = document.getElementsByClassName('imgBox')[0];
15     var reader = new FileReader(); //创建文件读取对象
16     var inp = document.getElementsByTagName('input')[0]; //获取文件源
17     inp.onchange = function(){ //input域发生改变后触发文件读取
18         reader.readAsDataURL(inp.files[0]); //使用文件读取对象读取图片为base64编码
19     }
20     reader.onload = function(e){ //当图片读取成功后触发
21         var img = new Image(); //创建img对象
22         img.src = e.target.result; //给img对象添加缓存中的bese64位编码的图片数据(异步)
23         img.onload = function(e){ //图片数据加载完成以后
24             if(this.width > this.height){ //当图片的宽度大于高度
25                 img.style.width = '100%'; //是:设置图片宽度100%,实现图片全部预览
26             }else{
27                 img.style.height = '100%';//否:设置图片高度100%,实现图片全部预览
28             }
29             imgBox.style.backgroundColor = '#000';
30             imgBox.innerHTML = null;
31             imgBox.appendChild(img);
32         }
33     }
34 </script>

4.实现文件加载进度条

在FileReader.onprogress事件对象中有两个属性loaded和total,loaded表示当前文件读取大小,total表示文件整体大小,并且在读取时会持续触发更新最新读取状态的数据,根据FileReader.onprogress事件就可以实现文件加载进度条的动画效果了,但是由于FileReader是h5的API在IE中最低兼容到10版本,所以需要根据具体的项目和兼容性来设计交互。

 1 //css
 2 .progress{
 3     position: relative;
 4     margin-top: 5px;
 5     width: 300px;
 6     height: 20px;
 7     border: 1px solid #300;
 8 }
 9 .progressText{
10     display: inline-block;
11     position: absolute;
12     width: 300px;
13     height: 20px;
14     text-align: center;
15     font-size: 10px;
16     line-height: 20px;
17 }
18 .progressSpan{
19     display: inline-block;
20     /* width: 200px; */
21     height: 20px;
22     background-color: #f0f;
23 }
24 //html
25 <input type="file" name="">
26 <!-- 文件加载进度条 -->
27 <div class="progress">
28     <span class="progressText"></span>
29     <span class="progressSpan"></span>
30 </div>
31 //js
32 //获取文件源(所有功能实现的公共代码区)
33 var inp = document.getElementsByTagName('input')[0]; //获取文件源
34 var reader = new FileReader(); //创建文件读取对象
35 // fileReader实现图片加载进度条
36 var progressSpanObj = document.getElementsByClassName('progressSpan')[0];
37 var progressTextObj = document.getElementsByClassName('progressText')[0];
38 inp.onchange = function(){
39     reader.readAsArrayBuffer(inp.files[0]);
40 }
41 reader.onloadstart = function(e){ //开始读取文件时触发
42     progressTextObj.innerText = "正在读取文件(0%)...";
43 }
44 reader.onprogress = function(e){ //读取进度事件
45     console.log(Math.round(e.loaded / e.total * 100));
46     var precent = Math.round(e.loaded / e.total * 100);
47     progressSpanObj.style.width = precent / 100 * 300 + 'px';
48     progressTextObj.innerText = '正在读取文件(' + precent + '%)...';
49 }
50 reader.onload = function(e){
51     progressTextObj.innerText = '文件读取完成(100%)';
52 }
53 reader.onerror = function(e){
54     progressTextObj.innerText = "文件读取出错误(~0v0~)";
55 }

二、fileReade的与file.slice实现文件切片读取

通过input-type[file]获取的文件对象上有这样几个数据:

inputDom.files[0];//获取File对象(在onchange事件后获取)

File对象上的属性与方法:

  • File():构造函数,返回一个新的文件对象
  • File.lastModified:返回所引用文件最后的修改日期,为自 1970年1月1日0:00 以来的毫秒数。没有已知的最后修改时间则会返回当前时间。
  • File.lastModifiedDate:返回当前File对象所引用文件最后修改事件的Date都西昂。
  • File.name:返回当前File对象所引用文件的名字。
  • File.size:返回文件的大小
  • File.webkitRelativePath:返回Filex相关的path或URL(这是个非标准属性,chrome上获取的是一个空字符串)
  • File.slice():文件对象上本身是没有方法的,slice方法同通过继承Blob对象上的slice方法实现的。

File对象说明手册(MDN):https://developer.mozilla.org/zh-CN/docs/Web/API/File

File.slice()方法说明手册(MDN):https://developer.mozilla.org/zh-CN/docs/Web/API/Blob/slice

File.slice实现文件切片读取:

 1 <!--样式同上面的进度条实例一样,这里就不添加了-->
 2 <input type="file" name="">
 3 <!-- 文件加载进度条 -->
 4 <div class="progress">
 5     <span class="progressText"></span>
 6     <span class="progressSpan"></span>
 7 </div>
 8 //获取文件源(所有功能实现的公共代码区)
 9 var inp = document.getElementsByTagName('input')[0]; //获取文件源
10 var reader = new FileReader(); //创建文件读取对象
11 // fileReader实现图片加载进度条
12 var progressSpanObj = document.getElementsByClassName('progressSpan')[0];
13 var progressTextObj = document.getElementsByClassName('progressText')[0];
14 //file.slice(起始字节,终止字节)与FileReader实现文件切片读取
15 function PartFileReader(files,type,event){
16     this.files = files;//inputObj.files[0]
17     this.type = type;  //配置FileReader读取文件的方法
18     this.event = event;//配置读取文件时需要触发的事件
19     this.total = files.size;//获取文件大小
20     this.step = 1024 * 1024;//1MB(单片大小/一兆)
21     this.loaded = 0; //文件当前读取进度
22     this.reader = new FileReader(); //实际读取文件的FileReader对象实例
23     this.abort = this.reader.abort; //中断文件读取(可以通过中断文件读取事件保留切片数据,实现下一次可以在原读取位置继续开始读取)
24     this.readPartFile(this.loaded); //开启读取文件
25     this.bindEvent();//绑定FileReader文件读取
26 }
27 //给切片读取对象原型上添加FileReader获取读取类型,开启读取文件
28 PartFileReader.prototype.readPartFile = function(start){
29     if(this.files.slice){
30         var file = this.files.slice(start,this.loaded + this.step);
31         switch(this.type){
32             case 'readAsBinaryString' :
33                 this.reader.readAsBinaryString(file);
34                 break;
35             case 'readAsDataURL' :
36                 this.reader.readAsDataURL(file);
37                 break;
38             case 'readAsArrayBuffer' :
39                 this.reader.readAsArrayBuffer(file);
40                 break;
41             case 'readAsText' :
42                 this.readAsText(file);
43                 break;
44         }
45     }
46 }
47 //给切片读取对象原型上绑定FileReader对象事件
48 PartFileReader.prototype.bindEvent = function(){
49     var self = this;
50     this.reader.onloadstart = function(e){
51         self.event.loadStart && self.event.loadStart.call(this,e);
52     }
53     this.reader.onprogress = function(e){
54         self.event.progress && self.event.progress.call(this,e);
55     }
56     this.reader.onload = function(e){
57         // 切片读取文件有别于非切片读取,切片读取的文件读取状态需要在每个切片读取成功后再刷新读取进度
58         self.loaded += e.loaded;
59         self.event.load && self.event.load.call(this,e,self.loaded,self.total);
60         if(self.loaded < self.total){
61             self.readPartFile(self.loaded);
62         }
63     }
64     this.reader.onloadend = function(e){
65         self.event.loadend && self.event.loadend.call(this,e);
66     }
67     this.reader.onabort = function(e){
68         self.event.abort && self.event.abort.call(this,e);
69     }
70 }
71 //调用文件切片读取对象,配置FileReader事件函数
72 inp.onchange = function(){
73     var reader = new PartFileReader(inp.files[0],'readAsArrayBuffer',{
74         loadStart:function(e){
75             progressTextObj.innerText = "正在读取文件(0%)...";
76         },
77         progress:function(e){},
78         load:function(e,loaded,total){
79             // 如果在读取的基础上写上传的话在这里获取读取成功的文件切片
80             // e.target.result //当前文件切片的数据
81             // (但是千万别在这里直接写网络请求,本地读取的速度远远大于网络请求,直接请求一个文件就会在瞬间发起大量请求)
82             // (最好的处理方式是将切片数据写入一个有序列表中,然后通过控制网络请求数量来实现)
83             var precent = Math.round(loaded / total * 100);
84             progressSpanObj.style.width = precent / 100 * 300 + 'px';
85             if(precent < 100){
86                 progressTextObj.innerText = '正在读取文件(' + precent + '%)...';
87             }else if(precent == 100){
88                 progressTextObj.innerText = '文件读取完成(100%)';
89             }
90         },
91         loadend:function(e){},
92         abort:function(e){},
93         error:function(e){
94             progressTextObj.innerText = "文件读取出错误(~0v0~)";
95         }
96     })
97 }