一直以来,JS都没有比较好的直接处理二进制的方法。Blob的存在,允许我们可以通过JS直接操作二进制数据。Blob 对象表示一个二进制文件的数据内容,比如一个图片文件的内容就可以通过 Blob 对象读写。通常用来读写文件,它的名字是 Binary Large Object (二进制大型对象)的缩写。
Blob对象是一个包含有只读原始数据的类文件对象。Blob对象中的数据并不一定是JavaScript中的原生形式。File接口基于Blob,继承Blob的功能,并且扩展支持用户计算机上的本地文件。 它与 ArrayBuffer 的区别在于,它用于操作二进制文件,而 ArrayBuffer 用于操作内存。
构造Blob对象
浏览器原生提供Blob()构造函数,用来生成实例对象。
new Blob(dataArray [, options]) // dataArray可以是任意多个ArrayBuffer,ArrayBufferView, Blob,或者 DOMString对象。
生成Blob对象有两种方法:一种是使用Blob构造函数,另一种是对已有的Blob对象使用slice()方法切出一段。
Blob构造函数
创建出完整数据,Blob的属性:size和type,表示数据的大小和类型;
let blob = new Blob(['I am Blob'],{type: 'text/html'});
console.log(blob); // size: 9; type: "text/html"
slice() 创建
可以用于大文件的切割分片处理。
Blob具有一个实例方法slice,用来拷贝原来的数据,返回一个新的Blob对象,包含源Blob对象中指定范围内的数据。
Blob.slice([start[, end[, contentType]]])
let newBlob = blob.slice(3); // size: 6
参数说明:
- start 可选,开始索引,可以为负数,语法类似于数组的slice方法.默认值为0.
- end 可选,结束索引,可以为负数,语法类似于数组的slice方法.默认值为最后一个索引.
- contentType可选 ,新的Blob对象的MIME类型,这个值将会成为新的Blob对象的type属性的值,默认为一个空字符串.
URL 创建Blob URL链接
createObjectURL
URL.createObjectURL() 静态方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的URL 对象表示指定的 File 对象或 Blob 对象。
URL.createObjectURL()函数可以创建一个Blob URL,参数blob是用来创建URL的File对象或者Blob对象,返回值格式是:blob://URL。window.URL = window.URL || window.webkitURL;
objectURL = URL.createObjectURL(blob);
在每次调用 createObjectURL() 方法时,都会创建一个新的 URL 对象,即使你已经用相同的对象作为参数创建过。当不再需要这些 URL 对象时,每个对象必须通过调用 URL.revokeObjectURL() 方法传入创建的URL为参数,用来释放它。浏览器会在文档退出的时候自动释放它们,但是为了获得最佳性能和内存使用状况,应该在安全的时机主动释放掉它们。
revokeObjectURL()
URL.revokeObjectURL() 静态方法用来释放一个之前通过调用 URL.createObjectURL() 创建的已经存在的 URL 对象。
window.URL = window.URL || window.webkitURL; // 兼容处理
URL.revokeObjectURL(objectURL);
Blob的使用
下载文件
// html
<a download="data.txt" id="getData">下载</a>
// JavaScript
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);
生成图片
var droptarget = document.getElementById('droptarget');
droptarget.ondrop = function (e) {
var files = e.dataTransfer.files;
for (var i = 0; i < files.length; i++) {
var type = files[i].type;
if (type.substring(0,6) !== 'image/')
continue;
var img = document.createElement('img');
img.src = URL.createObjectURL(files[i]);
img.onload = function () {
this.width = 100;
document.body.appendChild(this);
URL.revokeObjectURL(this.src);
}
}
}
上面代码通过为拖放的图片文件生成一个 URL,产生它们的缩略图,从而使得用户可以预览选择的文件。
读取文件
取得 Blob 对象后,可以通过FileReader对象,读取 Blob 对象的内容,即文件内容。
FileReader 对象提供四个方法,处理 Blob 对象。Blob 对象作为参数传入这些方法,然后以指定的格式返回。
- FileReader.readAsText():返回文本,需要指定文本编码,默认为 UTF-8。
- FileReader.readAsArrayBuffer():返回 ArrayBuffer 对象。
- FileReader.readAsDataURL():返回 Data URL。
FileReader.readAsBinaryString():返回原始的二进制字符串。
// HTML 代码如下
<input type="file" onchange="readfile(this.files[0])"></input>
<pre id="output"></pre>
// javascript
function readfile(f) {
var reader = new FileReader();
reader.readAsText(f);
reader.onload = function () {
var text = reader.result; // 读取文件的内容,以text格式显示到页面
var out = document.getElementById('output');
out.innerHTML = '';
out.appendChild(document.createTextNode(text));
}
reader.onerror = function(e) {
console.log('Error', e);
};
}
通过指定 FileReader 实例对象的onload监听函数,在实例的result属性上拿到文件内容
下边例子,用于读取二进制文件。// HTML 代码如下
<input type="file" onchange="typefile(this.files[0])"></input>
// JavaScript
function typefile(file) {
// 文件开头的四个字节,生成一个 Blob 对象
var slice = file.slice(0, 4);
var reader = new FileReader();
// 读取这四个字节
reader.readAsArrayBuffer(slice);
reader.onload = function (e) {
var buffer = reader.result;
// 将这四个字节的内容,视作一个32位整数
var view = new DataView(buffer);
var magic = view.getUint32(0, false);
// 根据文件的前四个字节,判断它的类型
switch(magic) {
case 0x89504E47: file.verified_type = 'image/png'; break;
case 0x47494638: file.verified_type = 'image/gif'; break;
case 0x25504446: file.verified_type = 'application/pdf'; break;
case 0x504b0304: file.verified_type = 'application/zip'; break;
}
console.log(file.name, file.verified_type);
};
}
分片上传
分片上传逻辑如下:
获取要上传文件的File对象,根据chunk(每片大小)对文件进行分片
- 通过post方法轮循上传每片文件,其中url中拼接querystring用于描述当前上传的文件信息;post body中存放本次要上传的二进制数据片段
- 接口每次返回offset,用于执行下次上传 ```javascript initUpload();
//初始化上传 function initUpload() { var chunk = 100 * 1024; //每片大小 var input = document.getElementById(“file”); //input file input.onchange = function (e) { var file = this.files[0]; var query = {}; var chunks = []; if (!!file) { var start = 0; //文件分片 for (var i = 0; i < Math.ceil(file.size / chunk); i++) { var end = start + chunk; chunks[i] = file.slice(start , end); start = end; }
// 采用post方法上传文件
// url query上拼接以下参数,用于记录上传偏移
// post body中存放本次要上传的二进制数据
query = {
fileSize: file.size,
dataSize: chunk,
nextOffset: 0
}
upload(chunks, query, successPerUpload);
}
}
}
// 执行上传 function upload(chunks, query, cb) { var queryStr = Object.getOwnPropertyNames(query).map(key => { return key + “=” + query[key]; }).join(“&”); var xhr = new XMLHttpRequest(); xhr.open(“POST”, “http://xxxx/opload?“ + queryStr); xhr.overrideMimeType(“application/octet-stream”);
//获取post body中二进制数据
var index = Math.floor(query.nextOffset / query.dataSize);
getFileBinary(chunks[index], function (binary) {
if (xhr.sendAsBinary) {
xhr.sendAsBinary(binary);
} else {
xhr.send(binary);
}
});
xhr.onreadystatechange = function (e) {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
var resp = JSON.parse(xhr.responseText);
// 接口返回nextoffset
// resp = {
// isFinish:false,
// offset:100*1024
// }
if (typeof cb === "function") {
cb.call(this, resp, chunks, query)
}
}
}
}
}
// 每片上传成功后执行 function successPerUpload(resp, chunks, query) { if (resp.isFinish === true) { alert(“上传成功”); } else { //未上传完毕 query.offset = resp.offset; upload(chunks, query, successPerUpload); } }
// 获取文件二进制数据 function getFileBinary(file, cb) { var reader = new FileReader(); reader.readAsArrayBuffer(file); reader.onload = function (e) { if (typeof cb === “function”) { cb.call(this, this.result); } } }
<a name="nuzt1"></a>
### 隐藏视频源路径
```javascript
var video = document.getElementById('video');
var obj_url = window.URL.createObjectURL(blob);
video.src = obj_url;
video.play()
window.URL.revokeObjectURL(obj_url);
Web Worker 串行加载优化
一般形式 :
main.js:
const worker = new Worker('worker.js');
worker.addEventListener('message', function(evt) {
console.log(`[main] result is: ${evt.data.result}.`);
}, false);
worker.postMessage({num1: 20, num2: 10});
console.log('[main] Main is initialized.');
worker.js:
self.addEventListener('message', function (evt) {
const num1 = evt.data.num1;
const num2 = evt.data.num2;
const result = num1 + num2;
console.log('[worker] num1=' + num1 + ', num2=' + num2);
self.postMessage({result: result});
}, false);
console.log(`[worker] Worker is initialized.`);
使用 Blob、URL.createObjectURL 优化后:
const workerFileContent = `self.addEventListener('message', function (evt) {
const num1 = evt.data.num1;
const num2 = evt.data.num2;
const result = num1 + num2;
console.log('[worker] num1=' + num1 + ', num2=' + num2);
self.postMessage({result: result});
}, false);`;
const workerBlob = new Blob([workerFileContent], { type:'text/javascript' });
const workerUrl = window.URL.createObjectURL(workerBlob);
const worker = new Worker(workerUrl);
window.URL.revokeObjectURL(workerUrl);
worker.addEventListener('message', function(evt) {
console.log(`[main] result is: ${evt.data.result}.`);
}, false);
worker.postMessage({num1: 20, num2: 10});