zoukankan      html  css  js  c++  java
  • javascript二进制文件处理方案

    众所周知,javascript是不支持二进制文件和数据的。传统的做法,是把二进制文件转成字符,然后在浏览器里,通过某些技巧把对应字符串换成数字,然后做其它处理(传统方法会在后面讲到)。这种处理方式不标准,需要某些“技巧”,甚至通过“欺骗”浏览器才能实现。

    随着浏览器的进化,出现了新的方案。XMLHttpRequest Level 2增加了对二进制数据的上传和下载支持,它可以和File System APIWeb 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处理:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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应该是最方便的了。下面是代码例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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位的数据。例子如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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请求到的字符串对应的字节数据了。下面是一段摘抄的代码例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    document.write(
        &quot;<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&quot;
    );

    这段代码都过document.write的方式,在页面中插入了两个三个vb函数,IEBinary_getByteAt返回指定位置的字节,IEBinary_getBytesAt返回从指定位置开始的指定长度的字节数组,IEBinary_getLength返回字符串中字节个数。我们把ajax的responseBody中的字符串通过这三个函数处理,就可以获得对应的二进制数据。

    注意:在ie低版本中,js处理能力不强,所以如果要处理的数据太大的话,很容易使导致cpu 100%,甚至浏览器崩溃。

    代码例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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

  • 相关阅读:
    IP的幻觉
    糟糕的一天
    windows下批量生成文件
    基于Bandersnatch搭建本地pypi源
    vmware vsphere 无法启动故障;
    关于Centos7客户端代理配置
    怎样在交换机判断是否出现环路了呢?
    小小的网络故障
    express for LINUX
    ESXI 7.0 ovf 导出;
  • 原文地址:https://www.cnblogs.com/litao229/p/2679105.html
Copyright © 2011-2022 走看看