版权声明:本文为吴孔云博客原创文章,转载请注明出处并带上链接,谢谢。 https://cloud.tencent.com/developer/article/1347540
最近有个需求,服务器端下载视频,存储到本地,然后再播放,下载存储后播放不了。debug后发现是responseType未正确设置。
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值的类型可为如下
值 | 数据类型 |
---|---|
’ ‘ | 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也是比较有意思,mdn上的解释是Blob对象表示不可变的类似文件对象的原始数据。Blob表示不一定是JavaScript原生形式的数据。
^_^其实就是英文Binary large Object,mysql有此类型数据结构
let blog = new Blob(arrya, options);
Blob() 构造函数返回一个新的 Blob 对象。
ArrayBuffer涉及面比较广,我的理解是ArrayBuffer代表内存之中的一段二进制数据,一旦生成不能再改。可以通过视图(TypedArray和DataView)进行操作。 TypedArray数组只是一层视图,本身不储存数据,它的数据都储存在底层的ArrayBuffer对象之中, 所以通过同一个arraybuffer生成的TypedArray共享内存数据。 nodejs中的buffer是对Uint8Array的实现。详细可参考另外一篇我写的文章
还有一点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, 在移动端有兼容问题
};
}
};
后面的内容与本文无关,纯作记录。
项目中的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);
});
}
读文件代码
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: ');
}
因安卓和ios下文件的存储路径不一样,故需要做一个判断,文件的存储目录
function getDirectory() {
let isIOS =/(iPad|iPhone|iPod)/g.test(navigator.userAgent);
if(isIOS) {
return cordova.file.documentsDirectory;
}else {
return cordova.file.dataDirectory + 'files/';
}
}