Streams API允许JavaScript以编程的方式访问通过网络接收的数据流,并根据开发人员的需要处理它们。
概念和用法
流将你希望通过网络接收的资源拆分成小块,然后按位处理它。这正是浏览器在接收用于显示web页面的资源时做的事情——视频缓冲区和更多的内容可以逐渐播放,有时候随着内容的加载,你可以看到图像逐渐地显示。
但曾经这些对于JavaScript是不可用的。以前,如果我们想要处理某种资源(如视频、文本文件等),我们必须下载完整的文件,等待它反序列化成适当的格式,然后在完整地接收到所有的内容后再进行处理。
随着流在JavaScript中的使用,一切发生了改变——只要原始数据在客户端可用,你就可以使用JavaScript 按位处理它,而不再需要缓冲区、字符串或blob。
还有更多的优点——你可以检测流何时开始或结束,将流链接在一起,根据需要处理错误和取消流,并对流的读取速度做出反应。
流的基础应用围绕着使响应可以被流处理展开。例如,一个成功的 fetch request 响应 Body 会暴露为 ReadableStream,之后你就可以使用 ReadableStream.getReader() 建立的 reader 读取它,使用 ReadableStream.cancel() 取消它等等。
fetch("https://www.example.org/").then((response) => {
const reader = response.body.getReader();
const stream = new ReadableStream({
start(controller) {
// 下面的函数处理每个数据块
function push() {
// "done"是一个布尔型,"value"是一个Uint8Array
reader.read().then(({ done, value }) => {
// 判断是否还有可读的数据?
if (done) {
// 告诉浏览器已经结束数据发送
controller.close();
return;
}
// 取得数据并将它通过controller发送给浏览器
controller.enqueue(value);
push();
});
};
push();
}
});
return new Response(stream, { headers: { "Content-Type": "text/html" } });
});
ArrayBuffer
ArrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区。
它是一个字节数组,通常在其他语言中称为“byte array”。
// create an ArrayBuffer with a size in bytes
const buffer = new ArrayBuffer(8);
console.log(buffer.byteLength);
// expected output: 8
这个和 node 中的 buffer 类似。
Blob
Blob 对象表示一个不可变、原始数据的类文件对象(类文件对象:其实就是类似于文件的对象。)。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream 来用于数据操作。
Blob 表示的不一定是 JavaScript 原生格式的数据。File 接口基于Blob,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。
File 对象是特殊类型的 Blob,且可以用在任意的 Blob 类型的 context 中。
Blob.stream() 返回一个能读取blob内容的 ReadableStream。
Blob.text() 返回一个promise且包含blob所有内容的UTF-8格式的 USVString。
Blob.arrayBuffer() 返回一个promise且包含blob所有内容的二进制格式的 ArrayBuffer
Blob() 构造函数允许通过其它对象创建 Blob 对象。比如,用字符串构建一个 blob:
var debug = {hello: "world"};
var blob = new Blob([JSON.stringify(debug, null, 2)], {type : 'application/json'});
FileReader
FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。
其中File对象可以是来自用户在一个 <input>
元素上选择文件后返回的FileList对象,也可以来自拖放操作生成的 DataTransfer对象。
使用 FileReader() 构造器去创建一个新的 FileReader。
FileReader.onload
处理 load 事件。该事件在读取操作完成时触发。
FileReader.result 只读
文件的内容。该属性仅在读取操作完成后才有效,数据的格式取决于使用哪个方法来启动读取操作。
FileReader.readAsArrayBuffer()
开始读取指定的 Blob中的内容, 一旦完成, result 属性中保存的将是被读取文件的 ArrayBuffer 数据对象
FileReader.readAsDataURL()
开始读取指定的Blob中的内容。一旦完成,result属性中将包含一个data: URL格式的Base64字符串以表示所读取文件的内容
FileReader.readAsText()
开始读取指定的Blob中的内容。一旦完成,result属性中将包含一个字符串以表示所读取的文件内容。
Uint8Array
数组类型表示一个8位无符号整型数组,创建时内容被初始化为0。
创建完后,可以以对象的方式或使用数组下标索引的方式引用数组中的元素。
相互转化
- dataURL(base64) 转化成 Blob(二进制)对象
function dataURLToBlob(fileDataURL) {
let arr = fileDataURL.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})
}
- File、Blob 文件数据绘制到 canvas
// 思路:File, Blob ——> dataURL ——> canvas
function fileAndBlobToCanvas(fileDataURL) {
let img = new Image()
img.src = fileDataURL
let canvas = document.createElement('canvas')
if(!canvas.getContext) {
alert('浏览器不支持canvas')
return;
}
let ctx = canvas.getContext('2d')
document.getElementById('container').appendChild(canvas)
img.onload = function() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
}
}
- File、Blob 转化成 dataURL
function fileToDataURL(file) {
let reader = new FileReader()
reader.readAsDataURL(file)
// reader 读取文件成功的回调
reader.onload = function(e) {
return reader.result
}
}
- 从 canvas 中获取文件 dataURL
function canvasToDataURL() {
let canvas = document.createElement('canvas')
let canvasDataURL = canvas.toDataURL('image/png', 1.0)
return canvasDataURL
}