关于二进制的一点小思考

2,329 阅读6分钟

1. 背景

在这次的公众号项目中,需要一个用户的小程序二维码,一开始想的是实时生成小程序二维码,后台问我行不行,隐约记得微信好像确实提供了这个功能,就答应下来了。一开始以为只需要access_tojen我就可以自己生成了,然而我好像忽略了跨域这件事。没办法,看来还是得后台中转一下了。因为后台暂时没空,我就要了appid和secret先自己用node试试效果。

getWXACodeUnlimit 文档说的是:

如果调用成功,会直接返回图片二进制内容,如果请求失败,会返回 JSON 格式的数据。

2. 问题来了

二进制内容是什么?ArrayBuffer还是Blob?都试试吧。

隐约记得,XHR2可以从服务器获取其他格式的数据了。

数据类型
'' DOMString (这个是默认类型)
arraybuffer ArrayBuffer对象
blob Blob对象
document Document对象
json JavaScript object, parsed from a JSON string returned by the server
text DOMString

我一开始还以为如果设置了responseType为Blob的话,返回的是字符串。当时可真的是一顿操作猛如虎,一看结果250。

其实只要查看一下响应数据的constructor属性就知道返回数据是什么类型的了,回想起来真的是好尴尬,竟然连这么基础的知识都忘了。

废话不多说,下面说我的解决方案吧。

3. 使用Blob作为图片的src

在这里想出来3种办法(_blob为ajax请求得到的Blob对象):

3.1 使用 window.URL 的方法

var src = window.URL.createObjectURL(blob);
document.querySelector("#img").setAttribute("src", src)

// createObjectURL创建的数据不会自行释放,所以无用的时候还得调用如下方法进行手动释放
window.URL.revokeObjectURL(src)

3.2 使用 FileReader 的方法

var reader = new FileReader();
reader.onload = (e) => {
  document.querySelector("#img").setAttribute("src", reader.result)
}
reader.readAsDataURL(_blob);

3.3 直接将接口地址设置为src的值

// 假设接口的地址是 http://test.com/index/getWxImg

// html
<img src="http://test.com/index/getWxImg" alt="微信小程序二维码" />

这就相当于在浏览器地址栏直接通过url访问该图片了,他会自动根据响应头的 Content-Type响应数据进行展示。当然了,这种方式只限于 get 请求,如果接口的请求方式只能是 post, 就只能用前两种方式了。

这次可真是闹了一个大乌龙,痛定思痛,我决定对 ArrayBufferBlob 进行一个较为深入的理解。

4. 名词解释

4.1 ArrayBuffer(二进制数组)

二进制数组(ArrayBuffer对象、TypedArray视图和DataView视图)是JavaScript操作二进制数据的一个接口。 这个接口的原始设计目的,与WebGL项目有关。所谓WebGL,就是指浏览器与显卡之间的通信接口,为了满足JavaScript与显卡之间大量的、实时的数据交换,它们之间的数据通信必须是二进制的,而不能是传统的文本格式。文本格式传递一个32位整数,两端的JavaScript脚本与显卡都要进行格式转化,将非常耗时。这时要是存在一种机制,可以像C语言那样,直接操作字节,将4个字节的32位整数,以二进制形式原封不动地送入显卡,脚本的性能就会大幅提升。

4.2 Blob(二进制数据)

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

Blob的构造函数为 Blob(blobParts[, options])。第一个参数必须为数组,可以是字符串数组,可以是二进制数组。options是一个对象,主要通过设置 type 值来指定文件的 content-type。

简单来说,可以直接把ArrayBuffer当成二进制数组,而把Blob当成是二进制数据。(这么说没错吧?如果理解的不对,欢迎指正。)

5. Blob与其他数据类型的转换

5.1 base64转为Blob

function dataURL2Blob(dataurl) {
  var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
      bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
  while (n--) {
      u8arr[n] = bstr.charCodeAt(n);
  }
  return new Blob([u8arr], { type: mime });
}

5.2 字符串转为Blob

function plain2Blob(text, type) {
  return new Blob([text], { type: 'text/plain' });
}

5.3 ArrayBuffer转为Blob

function buffer2Blob(_buffer, type) {
  return new Blob([_buffer], { type: 'application/octet-stream' });
}

5.4 Blob转为其他类型

从Blob中读取内容的唯一方法是使用 FileReader

var _blob = new Blob([]); //假设它是一个有效的Blob对象
var reader = new FileReader();
reader.onload = (e) => {
  console.log(reader.result)
}
  1. 转为base64: reader.readAsDataURL(_blob);
  2. 转为text: reader.readAsText(_blob);
  3. 转为arrayBuffer: reader.readAsArrayBuffer(_blob);

6. 利用Blob下载文件

6.1 下载函数

// @params content: String
// @params type: String
// @params filename: String
function downloadFile(content, type = 'text', filename) {
  var textBlob;
  if (type === 'img') {
    textBlob = dataURL2Blob(content);
    if (!filename) {
      throw new Error('type为img时, filename 文件名参数必传');
    }
  } else {
    textBlob = plain2Blob(content, type);
    filename = filename || generateFilename(type);
  }

  var a = document.createElement("a"),
      href = URL.createObjectURL(textBlob);
  a.href = href;
  a.download = filename;
  document.body.appendChild(a);
  a.click();

  document.body.removeChild(a);
  URL.revokeObjectURL(href);
}

function dataURL2Blob(dataurl) {
  var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
      bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
  while (n--) {
      u8arr[n] = bstr.charCodeAt(n);
  }
  return new Blob([u8arr], { type: mime });
}

function plain2Blob(text, type) {
  var fileTypes = {
    text: 'text/plain',
    svg: 'image/svg+xml',
  }

  return new Blob([text], { type: fileTypes[type] || 'application/octet-stream' })
}

function generateFilename(type) {
  var prefixs = {
    text: 'txt',
    svg: 'svg',
  }

  return '1.' + (prefixs[type] || '.txt')
}

6.2 测试

// 下载txt文件
downloadFile(`测试\r\n第二行`, 'text',)

// 下载svg
downloadFile(`<svg version="1.1" id="tuceng_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="80px" height="36px" viewBox="0 0 80 36" enable-background="new 0 0 80 36" xml:space="preserve">
<rect y="34.129" fill="#009944" width="36" height="2"/>
</svg>`, 'svg', '1.svg')

// 下载图片
var data = 'data:image/png;base64,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';
downloadFile(data, 'img', '1.png')

6.3 下载函数说明

目前只支持文本文件,svg,图片三种格式。

7 总结

Blob的作用还是蛮大的,在一定程度上可以在前端直接生成文件从而进行上传或者下载

File 也是 继承自 Blob, 所以生成的 图片Blob 也是可以直接通过 ajax 上传 OSS 之类的进行存储。之前有一个项目其中一部分是 在线PPT,我就是用 Blob 上传 PPT的首图 到 OSS 的。