一般的xhr請求
let url = window.URL || window.webkitURL;
let xhr = new XMLHttpRequest();
xhr.open(method, url, [,async=true,]);
xhr.ontimeout = ()=>{};
xhr.onreadystatechange ()=>{
if(xhr.readystate === 4) {
if(xhr.status =200) {
let res = xhr.responseText;
let blob = new Blob([res], {type: 'video/mpeg4'});
videoEle.src = url.createObjectURL(blob);
};
}
};
上面代碼處理一般的xhr請求足夠滿足,即返回類型為DOMString的,但是處理視頻下載並且存儲后播放就會有問題,上面代碼處理異步視頻下載有兩個bug,如果你都知道,就不需要往下看了^_^
再次認識responseType
responseType值的類型可為如下
| 值 |
數據類型 |
| ’ ‘ |
DOMString (這個是默認類型) |
| arraybuffer |
ArrayBuffer對象 |
| blob |
Blob對象 |
| document |
Document對象 |
| json |
JavaScript object, parsed from a JSON string returned by the server |
| text |
DOMString |
video后台為設置的content-type為application/octet-stream,表示二進制流。。當時就被這貨坑了一下,以為返回的數據能夠夠Blob構造函數接收,並正確顯示。
Blob對象
Blob也是比較有意思,mdn上的解釋是Blob對象表示不可變的類似文件對象的原始數據。Blob表示不一定是JavaScript原生形式的數據。
^_^其實就是英文Binary large Object,mysql有此類型數據結構
let blog = new Blob(arrya, options);
Blob() 構造函數返回一個新的 Blob 對象。
array 是一個由ArrayBuffer, ArrayBufferView, Blob, DOMString 等對象構成的 Array ,或者其他類似對象的混合體,它將會被放進 Blob。DOMStrings會被編碼為UTF-8。
options 是一個可選的BlobPropertyBag字典,它可能會指定如下兩個屬性:
type,默認值為 “”,它代表了將會被放入到blob中的數組內容的MIME類型。
ArrayBuffer涉及面比較廣,我的理解是ArrayBuffer代表內存之中的一段二進制數據,一旦生成不能再改。可以通過視圖(TypedArray和DataView)進行操作。
TypedArray數組只是一層視圖,本身不儲存數據,它的數據都儲存在底層的ArrayBuffer對象之中, 所以通過同一個arraybuffer生成的TypedArray共享內存數據。
nodejs中的buffer是對Uint8Array的實現。詳細可參考另外一篇我寫的文章
正確的video流打開方式
還有一點xhr.responseText的類型為DOMString,只有當responseType為DOMString時才有正確數據,其他類型獲取響應實體用xhr.response。因為一般我們都是獲取json字符串,此處也需要注意下。
so正確的代碼如下:
let url = window.URL || window.webkitURL; let xhr = new XMLHttpRequest(); xhr.open(method, url, [,async=true,]); xhr.responseType = 'blob' ; //arraybuffer也可以 xhr.ontimeout = ()=>{}; xhr.onreadystatechange=()=>{ if(xhr.readystate === 4) { if(xhr.status =200) { let res = xhr.response; //不是responseText。 /* *最近看別人的代碼,發現可以這么寫 * let res = 'response' in xhr ? xhr.response : xhr.responseText * 厲害!!! */ let blob = new Blob([res], {type: 'video/mpeg4'}); .... .... videoEle.src = url.createObjectURL(blob); //Videos on Android do not play when the src is set as a blob via create URL, 在移動端有兼容問題 }; } };
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
后面的內容與本文無關,純作記錄。
后續
項目中的video都存儲在移動設備中,如果都放在blob中,會造成內存的大量占用,因是cordova的webapp形式,故采用插件cordova-plugin-file,
相關寫文件代碼如下
function writeSystemFile(videoBlob, isAppend) { let self = this; window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fs) { //console.log('openFsObj', fs); //console.log('open file name: ', fs.name); fs.root.getFile(self._storeVideoName, { create: true, exclusive: false },function (fileEntry) { // var dataObj = new Blob([videoData], { type: 'text/plain' }); self.writeFile(fileEntry, videoBlob, isAppend); }, function(e) { console.log('onErrorCreateFile, error:', e); }); }, function(e) { console.log('onErrorLoadFs, error:', e); }); } function writeFile(fileEntry, dataObj, isAppend) { // let self = this; fileEntry.createWriter(function (fileWriter) { fileWriter.onwriteend = function() { console.log('Successful file write...'); //console.log('fileWriterEnd.length:', fileWriter.length); }; fileWriter.onerror = function (e) { console.log('Failed file write: ' + e.toString()); }; if(isAppend) { //表示是否追加文件 try { console.log('fileWriter.length:', fileWriter.length); fileWriter.seek(fileWriter.length); } catch(e) { console.log('file doesn`t exist:', e.toString()); } } //console.log('fileWriterStart.length:', fileWriter.length); fileWriter.write(dataObj); }); }
- 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 readSystemFile() { let self = this; console.log('readSystemFile self._storeVideoName:', self._storeVideoName); window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fs) { fs.root.getFile(self._storeVideoName, { create: true, exclusive: false },function (fileEntry) { self.readFile(fileEntry); }, self.onErrorCreateFile); }, function (error) { console.log('onErrorLoadFs, error:', error); }); } function readFile(fileEntry) { let self = this; fileEntry.file(function (file) { var reader = new FileReader(); reader.onloadend = function() { if(this.result === null) { console.log('readFile unexpected this.result == null'); return; } console.log(typeof this.result); console.log('Successful file read length: ', this.result.length); // var blob = new Blob([new Uint8Array(this.result)], { type: "video/mpeg4" }); console.log('Successful file read: ', this.result); }; // reader.readAsText(file); reader.readAsArrayBuffer(file); // reader.readAsBinaryString(file); }, self.onErrorReadFile); } onErrorReadFile() { console.log('Failed file read: '); }
- 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
因安卓和ios下文件的存儲路徑不一樣,故需要做一個判斷,文件的存儲目錄
function getDirectory() { let isIOS =/(iPad|iPhone|iPod)/g.test(navigator.userAgent); if(isIOS) { return cordova.file.documentsDirectory; }else { return cordova.file.dataDirectory + 'files/'; } }