HTML5 之 FileReader 的使用 (二) (網頁上圖片拖拽而且預顯示可在這裏學到) [轉載]

轉載至 : http://www.360doc.com/content/14/0214/18/1457948_352511645.shtmlcss

FileReader 資料(英文): https://developer.mozilla.org/en-US/docs/Web/API/FileReader#State_constantshtml

 

前面本博介紹了File API,這裏將繼續介紹一下FileReader,用FileReader具體地讀取文件內容。html5

         NOTE: 在chrome瀏覽器上本地測試的時候,即以file://xxx這種形式測試本文中的demo,會出現FileReader讀取不到內容的狀況,表現爲FileReader的result爲空或者FileReader根本就沒有去讀取文件內容,FileReader各個事件沒有觸發;這種狀況我想應該是相似於chrome不容許添加本地cookie那樣,chrome也不容許以file://xxx這種頁面上的js代碼訪問文件內容;解決辦法很簡單,只須要將測試文件放到一個web服務器上,以http://xxx形式訪問便可。

1、FileReader讀取文件內容

       File API一文中主要介紹獲取文件句柄的方法,接下來咱們就要利用該文件句柄來讀取文件內容,這是經過FileReader來實現的,經過FileReader接口,咱們能夠異步地將文件內容加載到內存中,賦予某個js變量。web

       FileReader具體支持哪些方法和事件,這裏就不介紹了,有興趣的能夠去w3c官網上看看FileReader介紹,這裏主要介紹一下FileReader兩個常見應用。chrome

一、預覽本地圖片

       這裏主要用到FileReader的readAsDataURL方法,經過將圖片數據讀取成Data URL的方法,將圖片展現出來,關於DATA URIapi

示例腳本:瀏覽器

 

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樣式,主要是爲了讓瀏覽器對圖片進行等比縮放處理,因此在瀏覽器中展現出來的圖片並非原始大小的圖片,而是通過瀏覽器自動等比縮放的圖片;若是須要查看原始尺寸圖片,可點擊相應圖片;再次單擊該圖片,則恢復小圖片。服務器

二、預覽文本文件

        這裏主要用到FileReader的readAsText,對於諸如mimetype爲text/plain、text/html等文件均認爲是文本文件,即minetype爲text開頭都能在本例中預覽。cookie

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' );
}

2、分段讀取文件內容(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]內,難道是由於這樣纔出現亂碼?

3、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,您能夠試一下:

分度讀取文件:

 

        這個示例中,沒有限制文件大小,讀取大文件時,也不會出現瀏覽器崩潰的狀況,能夠正常觀察到文件的讀取進度。

 

4、參考文章

一、 File API
二、 FileReader
三、 Blob
四、 ProgressEvent
相關文章
相關標籤/搜索