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

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

一、FileReader读取文件内容

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

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

1、预览本地图片

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

示例脚本:


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

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

点击此处查看demo>>

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

2、预览文本文件

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

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

简易的encodeHTML方法:

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

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

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

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

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

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

示例代码:

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

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. interface ProgressEvent : Event {  
2. boolean lengthComputable;  
3. long long loaded;  
4. long long total;  
5. };

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

HTML代码如下:

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

JS代码如下:
[javascript] view plaincopy

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

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

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

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

分度读取文件:

读取进度:

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