zoukankan      html  css  js  c++  java
  • 深入理解xhr responseType blob arrayBuffer document text json使用

    一般的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

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

    认识responseType
    设置该值能够改变响应类型(关键这句话)。就是告诉服务器你期望的响应格式。

    responseType值的类型

    XMLHttpRequest.responseType 属性是一个枚举类型的属性,返回响应数据的类型。它允许我们手动的设置返回数据的类型。如果我们将它设置为一个空字符串,它将使用默认的"text"类型。

    当将responseType设置为一个特定的类型时,你需要确保服务器所返回的类型和你所设置的返回值类型是兼容的。那么如果两者类型不兼容呢?恭喜你,你会发现服务器返回的数据变成了null,即使服务器返回了数据。还有一个要注意的是,给一个同步请求设置responseType会抛出一个InvalidAccessError 的异常.

    DOMString

    DOMString 是一个UTF-16字符串。由于JavaScript已经使用了这样的字符串,所以DOMString 直接映射到 一个String

    也就是说,在Ajax中,DOMString就等同于JS中的普通字符串。

    ArrayBuffer(又称类型化数组)

    ArrayBuffer对象用来表示通用的、固定长度的原始二进制数据缓冲区。ArrayBuffer 不能直接操作,而是要通过类型数组对象或 DataView 对象来操作,它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。

    用构造函数来创建一个指定字节长度的 ArrayBuffer 对象。 new ArrayBuffer()
    
    从本地文件读取, 利用FileReader.readAsArrayBuffer(),开始读取指定的 Blob中的内容, 一旦完成, result 属性中保存的将是被读取文件的 ArrayBuffer 数据对象.
    
    从base64 字符串获取
    通过ajax请求获取  设置responseType 为  ArrayBuffer 类型

    二进制数组由三类对象组成。

    (1)ArrayBuffer对象:代表内存之中的一段二进制数据,可以通过“视图”进行操作。“视图”部署了数组接口,这意味着,可以用数组的方法操作内存。

    (2)TypedArray视图:共包括9种类型的视图,比如Uint8Array(无符号8位整数)数组视图, Int16Array(16位整数)数组视图, Float32Array(32位浮点数)数组视图等等。

    (3)DataView视图:可以自定义复合格式的视图,比如第一个字节是 Uint8(无符号8位整数)、第二、三个字节是 Int16(16位整数)、第四个字节开始是 Float32(32位浮点数)等等,此外还可以自定义字节序。

    简单说,ArrayBuffer对象代表原始的二进制数据,TypedArray视图用来读写简单类型的二进制数据,DataView视图用来读写复杂类型的二进制数据。

    TypedArray 视图支持的数据类型一共有9种(DataView视图支持除Uint8C以外的其他8种)

     

    详细介绍见如链接: 

    https://blog.csdn.net/qiphon3650/article/details/78454273

    Blob(Binary Large Object): 二进制大数据对象

    blob的具体使用可参考如下两篇文章:

    https://segmentfault.com/a/1190000011563430

    https://www.cnblogs.com/hhhyaaon/p/5928152.html

    一直以来,JS都没有比较好的可以直接处理二进制的方法。而Blob的存在,允许我们可以通过JS直接操作二进制数据。

    Blob 对象表示一个不可变、原始数据的类文件对象。Blob 表示的不一定是JavaScript原生格式的数据。File 接口基于Blob,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。

    要从其他非blob对象和数据构造一个Blob,请使用 Blob() 构造函数。

    要创建包含另一个blob数据的子集blob,请使用 slice()方法。

    要获取用户文件系统上的文件对应的Blob对象,请参阅 File文(File 对象是特殊类型的 Blob

    Blob对象可以看做是存放二进制数据的容器,但是是类似文件对象的二进制数据,因此可以像操作File对象一样操作Blob对象,实际上File是继承自Blob。此外还可以通过Blob设置二进制数据的MIME类型

    构造函数创建blob:

    var blob = new Blob(dataArr:Array<any>, options:{type:string})

    Blob对象

    Blob也是比较有意思,mdn上的解释是Blob对象表示不可变的类似文件对象的原始数据。Blob表示不一定是JavaScript原生形式的数据。

    参数说明: 

    dataArray:数组,包含了要添加到Blob对象中的数据,数据可以是任意多个ArrayBuffer,ArrayBufferView, Blob,或者 DOMString对象。
    optons:可选项。
       type:默认值为 "",它代表了将会被放入到blob中的数组内容的MIME类型。
       endings,默认值为"transparent",用于指定包含行结束符
    的字符串如何被写入。 它是以下两个值中的一个: "native",表示行结束符会被更改为适合宿主操作系统文件系统的换行符; "transparent",表示会保持blob中保存的结束符不变。
    document:
    实际上就是XMLHttpRequest中数据返回属性之responseXML,也就是可以解析为XML的数据。因此,这里的Document数据类似你就可以近似看成XML数据类型。通过xhr.responseXML 获取 

    _其实就是英文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: ');
      }

    使用JavaScript类型数组接受二进制数据

    可以通过设置一个XMLHttpRequest对象的 responseType属性来改变一个从服务器上返回的响应的数据类型.可用的属性值为空字符串 (默认), "arraybuffer", "blob", "document","json" 和 "text". response属性的值会根据responseType属性包含实体主体(entity body), 它可能会是一个 ArrayBufferBlobDocument,JSON, string,或者为NULL(如果请求未完成或失败)

    下例读取了一个二进制图像文件,并且由该文件的二进制原生字节创建了一个8位无符号整数的数组.注意,这不会解码图像,但会读取像素。 你需要一个png解码库(png decoding library)。

    var oReq = new XMLHttpRequest();
    oReq.open("GET", "/myfile.png", true);
    oReq.responseType = "arraybuffer";
    
    oReq.onload = function (oEvent) {
      var arrayBuffer = oReq.response; // 注意:不是oReq.responseText
      if (arrayBuffer) {
        var byteArray = new Uint8Array(arrayBuffer);
        for (var i = 0; i < byteArray.byteLength; i++) {
          // 对数组中的每个字节进行操作
        }
      }
    };
    
    oReq.send(null);

    另一种方法是使用Blob接口直接构造一个接收arraybuffer 数据的 Blob

    var oReq = new XMLHttpRequest();
    oReq.open("GET", "/myfile.png", true);
    oReq.responseType = "arraybuffer";
    
    oReq.onload = function(oEvent) {
      var blob = new Blob([oReq.response], {type: "image/png"});
      // ...
    };
    
    oReq.send();

    也可以通过给responseType属性设置为“blob”,将二进制文件读取为Blob类型的数据。

    var oReq = new XMLHttpRequest();
    oReq.open("GET", "/myfile.png", true);
    oReq.responseType = "blob";
    
    oReq.onload = function(oEvent) {
      var blob = oReq.response;
      // ...
    };
    
    oReq.send();

    在老的浏览器中接受二进制数据

    下面的load_binary_resource()方法可以从指定的URL那里加载二进制数据,并将数据返回给调用者.

    function load_binary_resource(url) {
      var req = new XMLHttpRequest();
      req.open('GET', url, false);
      //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
      req.overrideMimeType('text/plain; charset=x-user-defined');
      req.send(null);
      if (req.status != 200) return '';
      return req.responseText;
    }

    最为奇妙的操作在第五行,该行重写了默认的MIME类型,强制浏览器将该响应当成纯文本文件来对待, 使用一个用户自定义的字符集.这样就是告诉了浏览器,不要去解析数据,直接返回未处理过的字节码.

    var filestream = load_binary_resource(url);
    var abyte = filestream.charCodeAt(x) & 0xff; // 扔掉的高位字节(f7)

    上例从请求回来的二进制数据中得到偏移量为x处的字节.有效的偏移量范围是0到filestream.length-1.

    查看 使用XMLHttpRequest下载文件 了解详情,查看下载文件.

    发送二进制数据

    XMLHttpRequest对象的send方法已被增强,可以通过简单的传入一个ArrayBufferBlob, 或者 File对象来发送二进制数据.

    下例创建了一个文本文件,并使用POST方法将该文件发送到了服务器上.你也可以使用文本文件之外的其他二进制数据类型.

    var oReq = new XMLHttpRequest();
    oReq.open("POST", url, true);
    oReq.onload = function (oEvent) {
      // 上传完成后.
    };
    
    var bb = new BlobBuilder(); // 需要合适的前缀: window.MozBlobBuilder 或者 window.WebKitBlobBuilder
    bb.append('abc123');
    
    oReq.send(bb.getBlob('text/plain'));

    将类型数组作为二进制数据发送

    你可以将JavaScript类型数组作为二进制数据发送出去.

    var myArray = new ArrayBuffer(512);
    var longInt8View = new Uint8Array(myArray);
    
    for (var i=0; i< longInt8View.length; i++) {
      longInt8View[i] = i % 255;
    }
    
    var xhr = new XMLHttpRequest;
    xhr.open("POST", url, false);
    xhr.send(myArray);

    上例新建了一个512字节的8比特整数的数组并发送它,当然,你也可以发送任意的二进制数据.

    注意: 从Gecko 9.0 (Firefox 9.0 / Thunderbird 9.0 / SeaMonkey 2.6)开始,添加了使用XMLHttpRequest发送 ArrayBuffer对象的功能.

    Firefox私有方法

    下面的例子使用了POST请求,用Firefox私有的非标准方法sendAsBinary()将二进制数据以异步模式传输了出去.

    var req = new XMLHttpRequest();
    req.open("POST", url, true);
    // 这里应该设置适当的MIME请求头
    req.setRequestHeader("Content-Length", 741);
    req.sendAsBinary(aBody);

    第四行将Content-Length请求头设置为741,表示发送的数据长度为741个字节.你应该根据你要发送的数据的大小改变这个值.

    第五行使用sendAsBinary()方法发送这个请求.

    你也可以通过将一个nsIFileInputStream对象实例传给send()方法来发送二进制内容,这样的话,你不需要自己去设置Content-Length请求头的大小,程序会自动设置:

    // 新建一个文件流.
    var stream = Components.classes["@mozilla.org/network/file-input-stream;1"]
                           .createInstance(Components.interfaces.nsIFileInputStream);
    stream.init(file, 0x04 | 0x08, 0644, 0x04); // file是一个nsIFile对象实例   
    
    // 设置文件的MIME类型
    var mimeType = "text/plain";
    try {
      var mimeService = Components.classes["@mozilla.org/mime;1"]
              .getService(Components.interfaces.nsIMIMEService);
      mimeType = mimeService.getTypeFromFile(file); // file是一个nsIFile对象实例
    }
    catch (oEvent) { /* 丢弃异常,使用默认的text/plain类型 */ }
    
    // 发送    
    var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
                        .createInstance(Components.interfaces.nsIXMLHttpRequest);
    req.open('PUT', url, false); /* 同步模式! */
    req.setRequestHeader('Content-Type', mimeType);
    req.send(stream);

     

  • 相关阅读:
    视图,触发器,事物,储存过程,函数,流程控制
    mysql之其他
    web前端之html
    mysql之索引
    Android minHeight/Width,maxHeight/Width
    Android GridView(九宫图)
    Android padding和margin的区别
    android:scaleType属性
    android:visibility
    Android RelativeLayout常用属性介绍
  • 原文地址:https://www.cnblogs.com/candlia/p/11919885.html
Copyright © 2011-2022 走看看