HTML5 FileReader

前面本博介绍了File API,这里将继续介绍一下FileReader,用FileReader具体地读取文件内容。

         NOTE: 在chrome浏览器上本地测试的时候,即以file://xxx这种形式测试本文中的demo,会出现FileReader读取不到内容的情况,表现为FileReader的result为空或者FileReader根本就没有去读取文件内容,FileReader各个事件没有触发;这种情况我想应该是类似于chrome不允许添加本地cookie那样,chrome也不允许以file://xxx这种页面上的js代码访问文件内容;解决办法很简单,只需要将测试文件放到一个web服务器上,以http://xxx形式访问即可。

一、FileReader读取文件内容

       File API一文中主要介绍获取文件句柄的方法,接下来我们就要利用该文件句柄来读取文件内容,这是通过FileReader来实现的,通过FileReader接口,我们可以异步地将文件内容加载到内存中,赋予某个js变量。

       FileReader具体支持哪些方法和事件,这里就不介绍了,有兴趣的可以去w3c官网上看看FileReader介绍,这里主要介绍一下FileReader两个常见应用。

1、预览本地图片

       这里主要用到FileReader的readAsDataURL方法,通过将图片数据读取成Data URL的方法,将图片展示出来,关于DATA URI

示例脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function  fileSelect(e) {
     e = e || window.event;
     
     var  files = e.target.files;   //FileList Objects    
     var  ireg = /image\/.*/i,
         p = document.getElementById( 'Preview' );
         
     var  ul = document.getElementById( 'Errors' );
     for ( var  i = 0, f; f = files[i]; i++) {
         if (!f.type.match(ireg)) {
             //设置错误信息
             var  li = document.createElement( 'li' );
             li.innerHTML =  '<li>'  + f.name + '不是图片文件.</li>' ;
             
             ul.appendChild(li);
             
             continue ;
         }
         
         var  reader =  new  FileReader();
         
         reader.onload = ( function (file) {
             return  function (e) {
                 var  span = document.createElement( 'span' );
                 span.innerHTML =  '<img class="thumb" src="' + this .result + '" alt="' + file.name + '" />' ;
                 
                 p.insertBefore(span,  null );
             };
         })(f);
         //读取文件内容
         reader.readAsDataURL(f);
     }
}
     
if (window.File && window.FileList && window.FileReader && window.Blob) {
     document.getElementById( 'Files' ).addEventListener( 'change' , fileSelect, false );
else  {
     document.write( '您的浏览器不支持File Api' );
}

        由以上代码可知,调用FileReader的readAsDataURL接口,将启动异步加载文件内容,通过给reader监听一个onload事件,将数据加载完毕后,在onload事件处理中,通过reader的result属性即可获得文件内容,查看示例>>

        NOTE:在示例中,我给图片指定了一个height:75px的css样式,主要是为了让浏览器对图片进行等比缩放处理,所以在浏览器中展示出来的图片并不是原始大小的图片,而是经过浏览器自动等比缩放的图片;如果需要查看原始尺寸图片,可点击相应图片;再次单击该图片,则恢复小图片。

2、预览文本文件

        这里主要用到FileReader的readAsText,对于诸如mimetype为text/plain、text/html等文件均认为是文本文件,即minetype为text开头都能在本例中预览。

NOTE:由于需要在页面上预览文本,如果使用innerHTML插入文本的话,则需要对html中一些特殊字符进行实体编码,这样才能保证正常显示文本。

简易的encodeHTML方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
function  encodeHTML(source) {
     return  source
             .replace(/&/g,  '&' )
             .replace(/</g,  '<' )
             .replace(/>/g,  '>' )
             .replace(/ "/, '" ')
             .replace(/ '/, ' '' );
};
function  fileSelect(e) {
     e = e || window.event;
     
     var  files = e.target.files;   //FileList Objects    
     var  ireg = /text\/.*/i,
         p = document.getElementById( 'Preview' );
         
     var  ul = document.getElementById( 'Errors' );
     for ( var  i = 0, f; f = files[i]; i++) {
         console.log(f.type);
         if (!f.type.match(ireg)) {
             //设置错误信息
             var  li = document.createElement( 'li' );
             li.innerHTML =  '<li>'  + f.name + '不是文本文件.</li>' ;
             
             ul.appendChild(li);
             
             continue ;
         }
         
         var  reader =  new  FileReader();
         
         reader.onload = ( function (file) {
             return  function (e) {
                 var  div = document.createElement( 'div' );
                 div.className =  "text"
                 div.innerHTML = encodeHTML( this .result);
                 
                 p.insertBefore(div,  null );
             };
         })(f);
         //读取文件内容
         reader.readAsText(f);
     }
}
     
if (window.File && window.FileList && window.FileReader && window.Blob) {
     document.getElementById( 'Files' ).addEventListener( 'change' , fileSelect, false );
else  {
     document.write( '您的浏览器不支持File Api' );
}

二、分段读取文件内容(slice)

      有的时候,一次性将一个大文件读入内存,并不是一个很好的选择(如果文件太大,使用FileReader读取文件内容,可能直接导致浏览器崩溃),w3c也想到了这种情况,所以html5允许对文件进行分段读取。

chrome以及firefox已经将File slice api调整为如下:

1
2
3
4
5
6
7
var  blob;
if (file.webkitSlice) {   //Blob中的方法
     blob = file.webkitSlice(start, end + 1,  'text/plain;charset=UTF-8' );
else  if (file.mozSlice) {
     blob = file.mozSlice(start, end + 1,  'text/plain;charset=UTF-8' );
}

        本例使用了FileReader的onloadend事件来检测读取成功与否,如果用onloadend则必须检测一下FileReader readyState,因为read abort时也会触发onloadend事件,如果我们采用onload,则可以不用检测readyState。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
function  readBlob(start, end) {
     var  files = document.getElementById( 'file' ).files;
     
     if (!files.length) {
         alert( '请选择文件' );
         return  false ;
     }
     
     var  file = files[0],
         start = parseInt(start, 10) || 0,
         end = parseInt(end, 10) || (file.size - 1);
         
     var  r = document.getElementById( 'range' ),
         c = document.getElementById( 'content' );
         
     var  reader =  new  FileReader();
     
     reader.onloadend =  function (e) {
         if ( this .readyState == FileReader.DONE) {
             c.textContent =  this .result;
             r.textContent =  "Read bytes: "  + (start + 1) +  " - "  + (end + 1) +  " of "  + file.size +  " bytes" ;
         }
     };
     var  blob;
     
     if (file.webkitSlice) {   //Blob中的方法
         blob = file.webkitSlice(start, end + 1,  'text/plain;charset=UTF-8' );
     else  if (file.mozSlice) {
         blob = file.mozSlice(start, end + 1,  'text/plain;charset=UTF-8' );
     }
     
     reader.readAsBinaryString(blob);
};
try  {
     document.getElementById( 'buttons' ).addEventListener( 'click' function (e) {
         if (e.target.tagName.toLowerCase() ==  'button' ) {
             var  start = e.target.getAttribute( 'data-start' ),
                 end = e.target.getAttribute( 'data-end' );
                 
             readBlob(start, end);
         }  
     });
catch (ex) {
     alert( 'something error happens!' )
}

NOTE:readAsBinaryString这个方法,读取的二进制字符串,在页面显示,出现中文乱码,不知道怎么解决,如果用reader.readAsText即可正常显示中文;在w3c官网上:binary string, in which every byte is represented by an integer in the range [0..255],而中文却不在[0...255]内,难道是因为这样才出现乱码?

三、FileReader进度条

       既然FileReader是异步读取文件内容,那么就应该可以监听它的读取进度。事实上,FileReader的onloadstart以及onprogress等事件,可以用来监听FileReader的读取进度。

       在onprogress的事件处理器中,提供了一个ProgressEvent对象,这个事件对象实际上继承了Event对象,提供了三个只读属性:lengthComputable、loaded、total;通过以上几个属性,即可实时显示读取进度。w3c官网上对它的定义如下:

1
2
3
4
5
interface ProgressEvent : Event {
   readonly attribute boolean lengthComputable;
   readonly attribute unsigned long long loaded;
   readonly attribute unsigned long long total;
};

      如果处理的文件太大,可能会导致浏览器崩溃(chrome下一般都会崩溃掉,而firefox则不会,不过会触发FileReader的onerror事件,文件读取失败),所以为了安全地、正常地观察到文件读取进度,我们采用分段读取的方法来测试FileReader的进度条。

HTML代码如下:

1
2
3
4
5
6
7
8
9
10
11
< form >
     < fieldset >
         < legend >分度读取文件:</ legend >
         < input  type = "file"  id = "File"  />
         < input  type = "button"  value = "中断"  id = "Abort"  />
         < p >
             < label >读取进度:</ label >< progress  id = "Progress" value = "0"  max = "100" ></ progress >
         </ p >
         < p  id = "Status" ></ p >
     </ fieldset >
</ form >
JS代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
var  h = {
     init:  function () {
         var  me =  this ;
         
         document.getElementById( 'File' ).onchange = me.fileHandler;
         document.getElementById( 'Abort' ).onclick = me.abortHandler;
         
         me.status = document.getElementById( 'Status' );
         me.progress = document.getElementById( 'Progress' );
         me.percent = document.getElementById( 'Percent' );
         
         me.loaded = 0;
         //每次读取1M
         me.step = 1024 * 1024;
         me.times = 0;
     },
     fileHandler:  function (e) {
         var  me = h;
         
         var  file = me.file =  this .files[0];
         
         var  reader = me.reader =  new  FileReader();
         
         //
         me.total = file.size;
         
         reader.onloadstart = me.onLoadStart;
         reader.onprogress = me.onProgress;
         reader.onabort = me.onAbort;
         reader.onerror = me.onerror;
         reader.onload = me.onLoad;
         reader.onloadend = me.onLoadEnd;
         //读取第一块
         me.readBlob(file, 0);
     },
     onLoadStart:  function () {
         var  me = h;
     },
     onProgress:  function (e) {
         var  me = h;
         
         me.loaded += e.loaded;
         //更新进度条
         me.progress.value = (me.loaded / me.total) * 100;
     },
     onAbort:  function () {
         var  me = h;
     },
     onError:  function () {
         var  me = h;
         
     },
     onLoad:  function () {
         var  me = h;
         if (me.loaded < me.total) {
             me.readBlob(me.loaded);
         else  {
             me.loaded = me.total;
         }
     },
     onLoadEnd:  function () {
         var  me = h;
         
     },
     readBlob:  function (start) {
         var  me = h;
         
         var  blob,
             file = me.file;
         
         me.times += 1;
         
         if (file.webkitSlice) {
             blob = file.webkitSlice(start, start + me.step + 1);
         else  if (file.mozSlice) {
             blob = file.mozSlice(start, start + me.step + 1);
         }
         
         me.reader.readAsText(blob);
     },
     abortHandler:  function () {
         var  me = h;
         
         if (me.reader) {
             me.reader.abort();
         }
     }
};
h.init();

        例子中的进度条采用html5 progress元素来实现的。

        每次读取1M的字节(你也可以随便改步长,比如说一次一个字节,然后传一个大小为几字节的文件,也能很好地观察到进度),在一次读取完毕后,onload事件中开启下一次读取,直至整个文件都读取完毕。

         如果您的浏览器支持html5,您可以试一下:

分度读取文件:

        这个示例中,没有限制文件大小,读取大文件时,也不会出现浏览器崩溃的情况,可以正常观察到文件的读取进度。

四、参考文章

1、 File API
2、 FileReader
3、 Blob
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值