本文主要从前端(浏览器)和后端(node)环境出发,分别讲述如何用js去调用环境提供的接口,而操控二进制数据
一.浏览器环境
前端环境下,js主要可以调用ArrayBuffer和Blob这两个对象接口
ArrayBuffer
ArrayBuffer 对象是ES6新加入的标准,表示一段二进制数据,用来模拟内存里面的数据。这个对象可以看作内存数据的表达
浏览器原生提供ArrayBuffer()构造函数,用来生成实例。它接受一个整数作为参数,表示这段二进制数据占用多少个字节
var buffer = new ArrayBuffer(8); //实例对象buffer占用8个字节
buffer.byteLength // 8,属性byteLength,表示当前实例占用的内存长度(单位字节)
ArrayBuffer 对象有实例方法slice(),用来复制一部分内存。它接受两个整数参数,分别表示复制的开始位置(从0开始)和结束位置(复制时不包括结束位置),如果省略第二个参数,则表示一直复制到结束
var buf1 = new ArrayBuffer(12); // 生成一个可以12个字节的连续内存,每个字节的默认值是0
var buf2 = buf1.slice(0); //实例buf2复制了buf1
注意:ArrayBuffer不能直接读写,只能通过视图进行操作,所以虽然可以调用,但是前端主要还是使用Blob
Blob
Blob(Binary Large Object 二进制大型对象),它与 ArrayBuffer 的区别在于,Blob 对象表示一个二进制文件的数据内容,比如一个图片文件的内容就可以通过 Blob 对象读写,它用于操作二进制文件,而 ArrayBuffer 用于操作内存
Blob构造函数接受两个参数。第一个参数是数组,成员是字符串或二进制对象,表示新生成的Blob实例对象的内容;第二个参数是可选的,是一个配置对象,目前只有一个属性type,它的值是一个字符串,表示数据的 MIME 类型,默认是空字符串
//用Blob保存json数据
var obj = { name: 'qgq' };
var blob = new Blob([ JSON.stringify(obj) ], {type : 'application/json'});
Blob具有两个实例属性size和type,分别返回数据的大小和类型,实例方法slice,用来拷贝原来的数据,返回的也是一个Blob实例
Blob的应用
1.浏览器点击下载功能
<a download="data.txt" id="getData">下载</a>
var data= 'Hello world!';
var blob = new Blob([data], {
type: 'text/html,charset=UTF-8'
});
window.URL = window.URL || window.webkitURL;
document.querySelector("#getData").href = URL.createObjectURL(blob);
上面的代码将Blob URL赋值给a标签,点击后,会提示下载文本文件为data.txt,文件内容为“Hello World”
2.分割大文件上传(利用Blob中slice方法)
function upload(blobOrFile){
var xhr = new XMLHttpRequest();
xhr.open('POST','/server/upload',true);
xhr.onload = function(e) { ... };
xhr.send(blobOrFile);
}
document.querySelector('input[type="file"]').addEventListener('change' , function(e){
var blob = this.files[0];
const BYTES_PER_CHUNK = 1024 * 1024; //1MB chunk size
const SIZE = blob.size;
var start = 0;
var end = BYTES_PER_CHUNK;
while(start < SIZE){
upload(blob.slice(start, end));
start = end;
end = start + BYTES_PER_CHUNK;
}
},false);
3.解决canvas图片跨域
如果想要获取一张图片上某个像素点的色值,可以使用canvas的getImageData来拿到数据,但是在canvas中,跨域图片是不能获取其图像信息的,如果取跨域的图片像素信息,就会出现跨域错误
//下面代码会报错The canvas has been tainted by cross-origin data.
const url = 'https://developer.mozilla.org/static/img/web-docs-sprite.22a6a085cf14.svg'
const img = new Image()
img.onload = function () {
ctx.drawImage(img, 0, 0)
addEventListener('click', pick)
}
img.src = url
function pick (e) {
const data = ctx.getImageData(e.layerX, e.layerY, 1, 1).data
console.log(data)
}
解决这个问题,首先服务端运行该资源跨域访问,另外,在img上添加一个crossOrigin
另可以通过Blob来解决
const url = 'https://developer.mozilla.org/static/img/web-docs-sprite.22a6a085cf14.svg'
const xhr = new XMLHttpRequest()
xhr.open('GET', url, true)
xhr.responseType = 'blob'
xhr.onload = function () {
if (this.status === 200) {
const blob = this.response
const img = new Image()
const imgObject = URL.createObjectURL(blob)
img.onload = function () {
ctx.drawImage(img, 0, 0)
canvas.addEventListener('click', pick)
document.body.removeChild(img)
URL.revokeObjectURL(imgObject)
}
img.src = imgObject
document.body.appendChild(img)
}
}
xhr.send()
function pick (e) {
const data = ctx.getImageData(e.layerX, e.layerY, 1, 1).data
console.log(data)
}
4.隐藏视频原路径,blob:http://
<video controls id='sound'/>
<script>
var xhr = new XMLHttpRequest();
xhr.open('POST','./server/video',true);
xhr.responseType = 'blob';
xhr.onload = function(e){
if(this.status == 200){
var blob = this.response;
document.getElementById("sound").src = URL.createObjectURL(blob);
}
}
xhr.send();
</script>
二.node环境
二进制数组,用来处理二进制数据的类
二进制数组由三类对象组成:
1.ArrayBuffer对象:前面也介绍了,代表内存中原始的二进制数据,本身不能直接读写,需要通过视图来操作
2.TypedArray视图:共包括 9 种类型的视图,比如Uint8Array(无符号 8 位整数)数组视图, Int16Array(16 位整数)数组视图, Float32Array(32 位浮点数)数组视图等等
3.DataView视图:可以自定义复合格式的视图,比如第一个字节是 Uint8(无符号 8 位整数)、第二、三个字节是 Int16(16 位整数)、第四个字节开始是 Float32(32 位浮点数)等等,此外还可以自定义字节序
Node中的Buffer类是以更优化和更适合Nodejs的方式实现了Uint8Array API,意思就是Buffer类其实是TypedArray(Uint8Array)的nodejs实现
ArrayBuffer有一个静态方法isView,判断参数是否为视图实例
const buffer = new ArrayBuffer(12);
buffer.byteLength; // 12
buffer.slice(0, 3); // 用法和数组一致,拷贝buffer的前三个字节生成一个新的ArrayBuffer对象
ArrayBuffer.isView(buffer); // false
const dataView = new DataView(buffer);
ArrayBuffer.isView(dataView); // true