JavaScript处理二进制数据

最近在开发扩展的时候遇到需要获取 MP3 数据并且直接交给其它接口处理的问题,但是使用 JQuery 的 AJAX 进行 获取数据的时候已经把数据进行了编码,毕竟默认都是处理文本数据.
但是我需要的是二进制数据并且进行 Base64 编码方便进行传输,因为 Base64 编码的值全是可打印字符. Base64 常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据.

# 二进制数据转换成 Base64 编码

于是便找到了一个非常有用的 MDN 的文档Sending and Receiving Binary Data (opens new window)
不过稍作修改:

var oReq = new XMLHttpRequest();
oReq.open('GET', request.data, true);
oReq.responseType = 'blob';
oReq.onload = function(oEvent) {
  var blob = oReq.response;
  var reader = new FileReader();
  reader.onload = function(readerEvt) {
    var binaryString = readerEvt.target.result;
    // console.log(binaryString);
    var testdata = btoa(binaryString);
    var data = {
      method: 'getAudio',
      data: 'data:audio/mp3;base64,' + btoa(binaryString)
    };
    port.postMessage(data);
  };
  reader.readAsBinaryString(blob);
};

oReq.send(null);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

设置 responseType 为Blob (opens new window) 然后使用 FileReader 作为二进制数据读取,再使用 btoa 转码成 Base64 进行传输.

还有一种是读取需要上传的文件进行处理的方式和通过 AJAX 获取的方式差不多,代码在这里 (opens new window).使用 Base64 编码的好处是在需要二进制数据的地方,例如图片和音频,我们可以使用 Base64 编码进行替换获取这些资源的 URL 可以减少 HTTP 请求,也可以做转发,我之所以有这个处理二进制数据的需求就是因为开发的一个插件无法在 HTTPS 页面上获取 HTTP 资源, 便通过 background js 进行获取数据然后 Base64 编码传输给 content js 进行使用.

# Base64 数据转换为 ArrayBuffer

有时候需要把传入的二进制数据转换成 ArrayBuffer 进行处理就需要转换了

var data = atob(base64String);
var dataView = new Uint8Array(data.length);
for (var i = 0; i < data.length; ++i) {
  dataView[i] = data.charCodeAt(i);
}
var arrayBuffer = dataView.buffer;
1
2
3
4
5
6

今天暂时就写这么多吧,期末考试看书好累,头脑都不太清醒了. QAQ

扇贝单词助手发布

最近受菊苣binux (opens new window)的要求,希望能开发一款扇贝网的单词助手 (opens new window),可以标记任意页面上已经背过的单词.项目源码在Github (opens new window). 然后我就趁着国庆就开坑了,binux 也给了我很多参考资料.因为之前也开发过 Chrome 的扩展,所以对 Chrome 提供的 API 比较熟悉.这里就介绍下使用的相关的 JavaScript 的知识吧~

# Promise

Promise 是用来处理大量的异步操作,因为异步操作不能直接 return 返回值,必须进行回调.所以就会导致很多异步操作在一起不断的进行嵌套,这样实在是很不优雅的代码书写方式.代码首先是用来给人读的,其次才是可以运行.使用 Promise 就可以把多个异步操作的返回值以数组的形式返回来,然后再进行回调.语言描述不好,还是上例子吧.

function get_cookie(site, name) {
  return new Promise(function(resolve, reject) {
    chrome.cookies.get({ url: site, name: name }, function(cookies) {
      if (cookies) {
        var data = cookies.name + '=' + cookies.value;
        resolve(data);
      } else {
        reject();
      }
    });
  });
}
1
2
3
4
5
6
7
8
9
10
11
12

Chrome 的 API 全部是异步操作,所以我一开始使用 return 的时候并没有得到值.把回调函数封装到一个 Promise 对象里面,然后传入两个参数 resolve,reject 分别代表异步操作成功执行或失败需要调用的函数.

var site1 = 'http://pan.baidu.com/';
var name1 = 'BDUSS';
var site2 = 'http://pcs.baidu.com/';
var name2 = 'pcsett';
Promise.all([get_cookie(site1, name1), get_cookie(site2, name2)]).then(
  function(value) {
    console.log(value);
  },
  function() {}
);
1
2
3
4
5
6
7
8
9
10

这里我们需要调用两次异步操作,一般情况的话需要函数嵌套,但是使用 Promise 对象的话,只需要在 then 里面需要写上 resolve 和 reject 需要的执行函数, 传入的 value 就是两次异步操作的返回值数组.然后就可以进行相关的处理.代码就变得清晰有条理.

# 参考

Promise 文档 (opens new window)

Promise.all (opens new window)

计算机中的字节序问题

计算机字节序这个问题,最近被老师反复提到.我也在本科阶段折腾交叉编译的时候遇到过.并且这个问题貌似也是面试的时候一个常见问题,所以还是写下来比较好.

字节序顾名思义就是计算机中字节的存储顺序.目前最常见的就是大端字节序和小端字节序.貌似百科里面又有一个中端字节序...(不过确实很少见)

# 大端字节序

按照最高位字节(包含最高位,即 MSB,的字节)至最低位字节(包含最低位,即 LSB,的字节)的顺序,存放在连续的地址中.

采用这种机制的处理器有 IBM3700 系列,和绝大多数的 RISC 处理(我的路由器就是)

# 小端字节序

按照最低位字节(包含 LSB 的字节)至最高位字节(包含 MSB 的字节)的顺序,存放在连续的地址中.

采用这种机制的处理器最常见的就是 Intel 系列处理器

# 判断字节序

当然字节序不能完全靠处理器来判断,因为有些处理器可能既支持大端字节序又支持小端字节序 最好用的方式还是用个程序跑一跑,实践是检验整理的唯一标准.

#include <stdio.h>
int main()
{
    unsigned int x = 0x12345678;
    char *p = (char *)&x;
    int i;
    for (i = 0; i < sizeof(x); i++){
        printf("%.2x ", *p++);
    }
    printf("\n");
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12

上面的一段 C 程序就可以检验字节序,
如果输出是:12 34 56 78,那么 CPU 是大端字节序.
如果输出时:78 56 34 12,那么 CPU 是小端字节序.
输出证明,大端字节序更适合人类阅读.

# 涉及字节序的问题

谈到字节序问题,是因为网络编程的时候如果双方的字节序不一样肯定会出问题的.本人在交叉编译学校的校园网程序的时候就是遇到了这个问题,因为字节序的不同,导致发送的数据包是相反的.后来在老师的点拨下才得以解决.