眾所周知,javascript是不支援二進位檔案和資料的。傳統的做法,是把二進位檔案轉成字元,然後在瀏覽器裡,通過某些技巧把對應字串換成數字,然後做其它處理(傳統方法會在後面講到)。這種處理方式不標準,需要某些“技巧”,甚至通過“欺騙”瀏覽器才能實現。
隨著瀏覽器的進化,出現了新的方案。XMLHttpRequest Level 2增加了對位元據的上傳和下載支援,它可以和File System API, Web Audio API等配合使用。我們的新方案就利用它這個功能。
先睹為快,這裡是個demo.
XHR2方案
XMLHttpRequest Level 2引入responseType和response兩個屬性,它們通知瀏覽器把請求到的資料按照某種格式進行處理。
-
xhr.responseType
-
在發送請求之前,根據需求把xhr.responseType設定為’text’、’arraybuffer’、’blob’或者’document’。它的預設值是’text’ 。
-
xhr.response
-
獲得了資料之後,根據之前responseType的值,xhr的response屬性就是DOMString、ArrayBuffer、Blob或者Document格式的資料
利用上面兩個屬性,我們可以把位元據格式化為ArrayBuffer,而不是字串。然後使用BlobBuilder api把資料轉化為blob處理:
?
1234567891011121314151617 |
BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder; var xhr = new XMLHttpRequest(); xhr.open( 'GET' , '/path/to/image.png' , true ); xhr.responseType = 'arraybuffer' ; xhr.onload = function (e) { if ( this .status == 200) { var bb = new BlobBuilder(); bb.append( this .response); // Note: not xhr.responseText var blob = bb.getBlob( 'image/png' ); ... } }; xhr.send(); |
這樣處理就方便多了。通過使用ArrayBufferView,我們可以更方便的處理資料。它對arraybuffer進行資料封裝,使我們可以像處理數組一樣處理請求到的位元據。view有好幾種格式,Float32Array、Float64Array、Int16Array、Int32Array、Int8Array、Uint16Array、Uint32Array、Uint8Array。個人覺得,處理位元據Uint8Array應該是最方便的了。下面是代碼例子:
?
12345678910111213 |
var xhr = new XMLHttpRequest(); xhr.open( 'GET' , '/path/to/image.png' , true ); xhr.responseType = 'arraybuffer' ; xhr.onload = function (e) { var uInt8Array = new Uint8Array( this .response); // this.response == uInt8Array.buffer var byte3 = uInt8Array[4]; // byte at offset 4 if (byte3 == 0xA9){ alert( 'the first data is 0xA9' ); } }; xhr.send(); |
它對瀏覽器的要求是firefox6+、chrome9+,那其它低版本的瀏覽器怎麼辦呢?降級使用傳統方案唄
傳統處理方案
傳統方案的思路:請求資料時把mime type重寫成自訂的格式的charset。然後處理請求資料中的每一個byte,與0xff進行與,獲得8位的資料。例子如下:
?
123456789101112131415161718 |
var xhr = new XMLHttpRequest(); xhr.open( 'GET' , '/path/to/image.png' , true ); // Hack to pass bytes through unprocessed. xhr.overrideMimeType( 'text/plain; charset=x-user-defined' ); xhr.onreadystatechange = function (e) { if ( this .readyState == 4 && this .status == 200) { var binStr = this .responseText; for ( var i = 0, len = binStr.length; i < len; ++i) { var c = binStr.charCodeAt(i); //String.fromCharCode(c & 0xff); var byte = c & 0xff; // byte at offset i } } }; xhr.send(); |
通過設定mimetype技巧,我們擷取了代表位元據的字串,然後處理字串,得到我們想到的資料。此方法不被推薦,因為這種方法每次都要把字元強制轉換成我們需要的格式,而且需要通過小技巧“騙”一下瀏覽器。
ie方案
ie9-不支援overrideMimeType方法,怎麼辦呢?前幾天ququ的文章《圖片自動旋轉的前端實現方案》中提到使用vb做資料處理,實驗了一下,還真行。vb裡有很多位元組處理的函數,這裡只介紹用到的三個:
-
MidB
-
返回字串( String)的一部分。用法result = MidB( 源字串, 起點, [長度] ),MidB 將源字串當作一組位元組,而不是一組字元來處理。MidB 應被用在源字串代表位元據的情況下。
-
AscB
-
AscB 返回首位元組。它與MidB一起使用,可以獲得資料中指定位置的位元組。
-
LenB
-
獲得字串中的位元組總數。LenB 函數將字串當作一組位元組而不是一組字元。當字串代表位元據時應當使用此函數。
通這個三個函數,我們可以擷取ajax請求到的字串對應的位元組資料了。下面是一段摘抄的代碼例子:
?
123456789101112131415161718 |
document.write( "<script type= 'text/vbscript' >\r\n " + " Function IEBinary_getByteAt(strBinary, iOffset)\r\n " + " IEBinary_getByteAt = AscB(MidB(strBinary, iOffset + 1, 1))\r\n " + " End Function\r\n " + " Function IEBinary_getBytesAt(strBinary, iOffset, iLength)\r\n " + " Dim aBytes()\r\n " + " ReDim aBytes(iLength - 1)\r\n " + " For i = 0 To iLength - 1\r\n " + " aBytes(i) = IEBinary_getByteAt(strBinary, iOffset + i)\r\n " + " Next\r\n " + " IEBinary_getBytesAt = aBytes\r\n " + " End Function\r\n " + " Function IEBinary_getLength(strBinary)\r\n " + " IEBinary_getLength = LenB(strBinary)\r\n " + " End Function\r\n " + " </script>\r\n" ); |
這段代碼都過document.write的方式,在頁面中插入了兩個三個vb函數,IEBinary_getByteAt返回指定位置的位元組,IEBinary_getBytesAt返回從指定位置開始的指定長度的位元組數組,IEBinary_getLength返回字串中位元組個數。我們把ajax的responseBody中的字串通過這三個函數處理,就可以獲得對應的位元據。
注意:在ie低版本中,js處理能力不強,所以如果要處理的資料太大的話,很容易使導致cpu 100%,甚至瀏覽器崩潰。
代碼例子:
?
1234567891011121314 |
var xhr = new ActiveXObject( "Microsoft.XMLHTTP" ); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == "200" || xhr.status == "206" || xhr.status == "0" ) { var blen = IEBinary_getLength(xhr.responseBody), byteArray = new VBArray(IEBinary_getBytesAt(xhr.responseBody, 0, blen)).toArray(); rpg.drawNpc(byteArray); } xhr = null ; } }; xhr.open( 'GET' , url, true ); xhr.send( null ); |
參考文章:
New Tricks in XMLHttpRequest2
JavaScript typed arrays
圖片自動旋轉的前端實現方案
Read EXIF data with Javascript