专栏首页大前端_Web深入理解xhr的responseType中blob和arrayBuffer

深入理解xhr的responseType中blob和arrayBuffer

版权声明:本文为吴孔云博客原创文章,转载请注明出处并带上链接,谢谢。 https://blog.csdn.net/wkyseo/article/details/78232485

最近有个需求,服务器端下载视频,存储到本地,然后再播放,下载存储后播放不了。debug后发现是responseType未正确设置。

一般的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, 在移动端有兼容问题
        };
      }
    };

后面的内容与本文无关,纯作记录。

后续

项目中的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/';
    }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • XMLHttpRequest使用指南大全

    我们通常将Ajax等同于XMLHttpRequest,但细究起来它们两个是属于不同维度的2个概念。

    空空云
  • 项目中遇到的bug(web前端-持续更新)

    版权声明:本文为吴孔云博客原创文章,转载请注明出处并带上链接,谢谢。 https://blog.csdn.net/wkyseo/articl...

    空空云
  • NodeJS学习三之API

    版权声明:本文为吴孔云博客原创文章,转载请注明出处并带上链接,谢谢。 https://blog.csdn.net/wkyseo/articl...

    空空云
  • 探究{ a = 1; function a(){} }和{ function b(){}; b = 1 }

    相信大部分人都了解了,这里再重复啰嗦一下。js是解析执行的,变量提升是js中执行上下文的工作方式。变量声明和函数声明在编译阶段会被提前。

    lhyt
  • 我理解的JavaScript预编译

    JavaScript执行过程首先先语法分析,就是分析一遍代码有没有语法错误,解析期间不会执行代码。接着就开始预编译,预编译完了就开始一行一行执行代码。

    wade
  • 意译:自调用函数表达式

    一、写在前面   本文将一如既往地遵循从自身理解出发,而非100%按原文逐句翻译的方式进行“伪翻译”,若有谬误请各位指正,谢谢!! 二、介绍   IIFE(th...

    ^_^肥仔John
  • 深入理解JavaScript系列(46):代码复用模式(推荐篇)

    同时,ECMAScript5也提供了类似的一个方法叫做Object.create用于继承对象,用法如下:

    用户4962466
  • 谈谈ES6语法(汇总下篇)

    ES2017标准引入了async函数,使得异步操作更加方便。async函数是Generator函数的语法糖。不打算写Generator函数,感兴趣的话可以看文档...

    嘉明
  • js中的函数声明你真的会了吗???

    2、只有在function内部新声明的才是局部变量,在if,while,for等声明的变量其实是全局变量(除非本身在function内部)

    李才哥

扫码关注云+社区

领取腾讯云代金券