《你不知道的 Blob》番外篇

学习时间:2020.06.06
学习章节:《你不知道的 Blob》

原文对 Blob 的知识点介绍得很是完整清晰,本文经过四个问题来总结本文核心知识:javascript

  1. Blob 是什么?
  2. Blob 怎么用?
  3. Blob 有哪些使用场景?
  4. Blob 与 ArrayBuffer 有何区别?

读《你不知道的 Blob》总结.png

1、Blob 是什么?

Blob(Binary Large Object)表示二进制类型的大对象,一般是影像、声音或多媒体文件。MySql/Oracle数据库中,就有一种Blob类型,专门存放二进制数据。在 JavaScript 中 Blob 对象表示一个不可变、原始数据的类文件对象,它不必定非得是大量数据,也能够表示一个小型文件的内容。
另外,JavaScript 中的 File 接口是基于 Blob,继承 Blob 的功能并将其扩展使其支持用户系统上的文件。
html

2、Blob 怎么用?

Blob 由一个可选字符串 typeblobParts 组成,其中, type 一般为 MIME 类型。前端

MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型,常见有:超文本标记语言文本 .html text/html 、PNG图像 .png image/png 、普通文本 .txt text/plain  等。java

1. 构造函数

Blob 构造函数语法为:web

const myBlob = new Blob(blobParts[, options])
复制代码

入参:数据库

  • blobParts:它是一个由 ArrayBuffer,ArrayBufferView,Blob,DOMString 等对象构成的数组。DOMStrings 会被编码为 UTF-8。
  • options :一个可选的对象,包含如下两个属性:
    • type :默认值为 "" ,表示将会被放入到 blob 中的数组内容的 MIME 类型。
    • endings :默认值为 "transparent",用于指定包含行结束符 \n 的字符串如何被写入。它是如下两个值中的一个: "native",表明行结束符会被更改成适合宿主操做系统文件系统的换行符,或者 "transparent",表明会保持 blob 中保存的结束符不变。

出参:
返回一个新建立的 Blob 对象,其内容由参数中给定的数组串联组成。
canvas

2. 属性和方法

2.1 属性介绍

Blob 对象拥有 2 个属性:数组

  • size :只读,表示 Blob 对象中所包含的数据大小(以字节为单位);
  • type :只读,值为字符串,表示该 Blob 对象所包含数据的 MIME 类型。若类型未知,则该属性值为空字符串。

2.2 方法介绍

  • slice([start[, end[, contentType]]]) :返回一个新的 Blob 对象,包含了源 Blob 对象中指定范围内的数据。
  • stream():返回一个能读取 Blob 内容的 ReadableStream
  • text():返回一个 Promise 对象且包含 Blob 全部内容的 UTF-8 格式的 USVString
  • arrayBuffer():返回一个 Promise 对象且包含 Blob 全部内容的二进制格式的 ArrayBuffer


注意:** Blob 对象是不可改变的**,可是能够进行分割,并建立出新的 Blob 对象,将它们混合到一个新的 Blob  中。相似于 JavaScript 字符串:咱们没法更改字符串中的字符,但能够建立新的更正后的字符串。
浏览器

3. 简单上手

3.1 示例1:从字符串建立 Blob

let myBlobParts = ['<html><h2>Hello Leo</h2></html>']; // 一个包含DOMString的数组
let myBlob = new Blob(myBlobParts, {type : 'text/html', endings: "transparent"}); // 获得 blob  console.log(myBlob.size + " bytes size"); // Output: 31 bytes size console.log(myBlob.type + " is the type"); // Output: text/html is the type 复制代码

3.2 示例2:从类型化数组和字符串建立 Blob

JavaScript类型化数组是一种相似数组的对象,并提供了一种用于 访问原始二进制数据的机制 。而且在类型数组上调用 Array.isArray() 会返回 false
详细可参考MDN《JavaScript 类型化数组》章节。缓存

let hello = new Uint8Array([72, 101, 108, 108, 111]); // 二进制格式的 "hello"
let blob = new Blob([hello, ' ', 'leo'], {type: 'text/plain'}); // Output: "Hello leo" 复制代码

3.3 示例3:组装新的 Blob

因为 Blob 对象是不可改变的,但咱们能够进行分割,并组装成一个新的 Blob 对象:

let blob1 = new Blob(['<html><h2>Hello Leo</h2></html>'], 
 {type : 'text/html', endings: "transparent"}); let blob2 = new Blob(['<html><h2>Happy Boy!</h2></html>'],  {type : 'text/html', endings: "transparent"}); let slice1 = blob1.slice(16); let slice2 = blob2.slice(0, 16);  await slice1.text(); // currtent slice1 value: "Leo</h2></html>" await slice2.text(); // currtent slice2 value: "<html><h2>Happy "  let newBlob = new Blob([slice2, slice1],  {type : 'text/html', endings: "transparent"}); await newBlob.text(); // currtent newBlob value: "<html><h2>Happy Leo</h2></html>" 复制代码

3、Blob 有哪些使用场景?

1. 图片本地预览

这里整理 2 种图片本地预览的方式:

  1. 使用 DataURL 方式;
  2. 使用 Blob URL/Object URL 方式;
<body>
 <h1>1.DataURL方式:</h1>  <input type="file" accept="image/*" onchange="selectFileForDataURL(event)">  <img id="output1">   <h1>2.Blob方式:</h1>  <input type="file" accept="image/*" onchange="selectFileForBlob(event)">  <img id="output2">   <script>  // 1.DataURL方式:  async function selectFileForDataURL() {  const reader = new FileReader();  reader.onload = function () {  const output = document.querySelector("#output1")  output.src = reader.result;  }  reader.readAsDataURL(event.target.files[0]);  }   //2.Blob方式:  async function selectFileForBlob(){  const reader = new FileReader();  const output = document.querySelector("#output2");  const imgUrl = window.URL.createObjectURL(event.target.files[0]);  output.src = imgUrl;  reader.onload = function(event){  window.URL.revokeObjectURL(imgUrl);  }  }  </script> </body> 复制代码


上面主要介绍 Blob URLData URL 两种方式实现图片本地预览,这两个类型的区别在**《5、拓展》**中介绍。

2. 图片本地预览 + 分片上传

实现本地预览:
input 获取到的 file 对象,经过实例化 FileReader ,赋值给变量 reader ,调用readerreadAsDataURL 方法,将 file 对象转换为  dataURL ,而后监听 readeronload 属性,获取到读取结果 result ,而后设置为图片的 src 值。

实现分片上传:
因为 File 是特殊类型的 Blob,可用于任意 Blob 类型的上下文,因此针对大文件传输,咱们可使用 slice 方法进行文件切割,分片上传。

<body>
 <input type="file" accept="image/*" onchange="selectFile(event)">  <button onclick="upload()">上传</button>  <img id="output">   <script>  const chunkSize = 10000;  const url = "https://httpbin.org/post";  async function selectFile(){  // 本地预览  const reader = new FileReader();  reader.onload = function(){  const output = document.querySelector("#output")  output.src = reader.result;  }  reader.readAsDataURL(event.target.files[0]);   // 分片上传  await upload(event.target.files[0]);  }  async function upload(files){  const file = files;  for(let start = 0; start < file.size; start += chunkSize){  const chunk = file.slice(start, start + chunkSize + 1);  const fd = new FormData();  fd.append("data", chunk);  await fetch(url, { method: "post", body: fd }).then((res) =>{  console.log(res)  res.text();  });  }  }  </script> </body> 复制代码

3. 图片本地预览 + 分片上传 + 暂停 + 续传

<body>
 <input type="file" accept="image/*" onchange="selectFile(event)">  <button onclick="upload()">上传</button>  <button onclick="pause()">暂停</button>  <button onclick="continues()">继续</button>  <img id="output" src="" alt="">   <script>  const chunkSize = 30000;  let start = 0, curFile, isPause = false;  const url = "https://httpbin.org/post";  async function selectFile(){  const reader = new FileReader();  reader.onload = function(){  const output = document.querySelector("#output")  output.src = reader.result;  }  reader.readAsDataURL(event.target.files[0]);  curFile = event.target.files[0];  }  async function upload(){  const file = curFile;  for(start; start < file.size; start += chunkSize){  if(isPause) return;  const chunk = file.slice(start, start + chunkSize + 1);  const fd = new FormData();  fd.append("data", chunk);  await fetch(url, { method: "post", body: fd }).then((res) =>{  res.text()  }  );  if(chunk.size < chunkSize){  uploadSuccess();  return;  }  }  }  function pause(){  isPause = true;  }  function continues(){  isPause = false;  upload();  }  function uploadSuccess(){  isPause = false;  start = 0;  }  </script> </body> 复制代码

4. 从互联网下载数据

在实现“从互联网下载数据”方法时,咱们使用 createObjectURL 显示图片,在请求互联网图片时,咱们有两种方式:

  • 使用 XMLHttpRequest
  • 使用 fetch
<body>
 <button onclick="download1()">使用 XMLHttpRequest 下载</button>  <button onclick="download2()">使用 fetch 下载</button>  <img id="pingan">  <script>  const url = "http://images.pingan8787.com/TinyCompiler/111.png";  const pingan = document.querySelector('#pingan');  function download1 (){  const xhr = new XMLHttpRequest();  xhr.open('GET', url);  xhr.responseType = 'blob';  xhr.onload = () => {  renderImage(xhr.response);  }  xhr.send(null);  }  function download2 (){  fetch(url).then(res => {  return res.blob();  }).then(myBlob => {  renderImage(myBlob);  })  }   function renderImage (data){  let objectURL = URL.createObjectURL(data);  pingan.src = objectURL;  // 根据业务须要手动调用 URL.revokeObjectURL(imgUrl)  }  </script> </body> 复制代码

5. 下载文件

经过调用 Blob 的构造函数来建立类型为 "text/plain" 的 Blob 对象,而后经过动态建立 a 标签来实现文件的下载。

<body>
 <button onclick="download()">Blob 文件下载</button>   <script>  function download(){  const fileName= "Blob文件.txt";  const myBlob = new Blob(["一文完全掌握 Blob Web API"], { type: "text/plain" });  downloadFun(fileName, myBlob);  }  function downloadFun(fileName, blob){  const link = document.createElement("a");  link.href = URL.createObjectURL(blob);  link.download = fileName;  link.click();  link.remove();  URL.revokeObjectURL(link.href);  }  </script> </body> 复制代码

6. 图片压缩

当咱们但愿本地图片在上传以前,先进行必定压缩,再提交,从而减小传输的数据量。
在前端咱们可使用 Canvas 提供的 toDataURL() 方法来实现,该方法接收 typeencoderOptions 两个可选参数:

  • type 表示 图片格式,默认为 image/png
  • encoderOptions 表示 图片质量,在指定图片格式为 image/jpegimage/webp 的状况下,能够从 0 到 1 区间内选择图片质量。若是超出取值范围,将会使用默认值 0.92,其余参数会被忽略。
<body>
 <input type="file" accept="image/*" onchange="loadFile(event)" />  <script>  // compress.js  const MAX_WIDTH = 800; // 图片最大宽度  // 图片压缩方法  function compress(base64, quality, mimeType) {  let canvas = document.createElement("canvas");  let img = document.createElement("img");  img.crossOrigin = "anonymous";  return new Promise((resolve, reject) => {  img.src = base64;  img.onload = () => {  let targetWidth, targetHeight;  if (img.width > MAX_WIDTH) {  targetWidth = MAX_WIDTH;  targetHeight = (img.height * MAX_WIDTH) / img.width;  } else {  targetWidth = img.width;  targetHeight = img.height;  }  canvas.width = targetWidth;  canvas.height = targetHeight;  let ctx = canvas.getContext("2d");  ctx.clearRect(0, 0, targetWidth, targetHeight); // 清除画布  ctx.drawImage(img, 0, 0, canvas.width, canvas.height);  let imageData = canvas.toDataURL(mimeType, quality / 100); // 设置图片质量  resolve(imageData);  };  });  }   // 为了进一步减小传输的数据量,咱们能够把它转换为 Blob 对象  function dataUrlToBlob(base64, mimeType) {  let bytes = window.atob(base64.split(",")[1]);  let ab = new ArrayBuffer(bytes.length);  let ia = new Uint8Array(ab);  for (let i = 0; i < bytes.length; i++) {  ia[i] = bytes.charCodeAt(i);  }  return new Blob([ab], { type: mimeType });  }   // 经过 AJAX 提交到服务器  function uploadFile(url, blob) {  let formData = new FormData();  let request = new XMLHttpRequest();  formData.append("image", blob);  request.open("POST", url, true);  request.send(formData);  }   function loadFile(event) {  const reader = new FileReader();  reader.onload = async function () {  let compressedDataURL = await compress(  reader.result,  90,  "image/jpeg"  );  let compressedImageBlob = dataUrlToBlob(compressedDataURL);  uploadFile("https://httpbin.org/post", compressedImageBlob);  };  reader.readAsDataURL(event.target.files[0]);  };  </script> </body> 复制代码

其实 Canvas 对象除了提供 toDataURL() 方法以外,它还提供了一个 toBlob() 方法,该方法的语法以下:

canvas.toBlob(callback, mimeType, qualityArgument)
复制代码

toDataURL() 方法相比,toBlob() 方法是异步的,所以多了个 callback 参数,这个 callback 回调方法默认的第一个参数就是转换好的 blob文件信息。

7. 生成 PDF 文档

在浏览器端,利用一些现成的开源库,好比 jsPDF,咱们也能够方便地生成 PDF 文档。

<body>
 <h3>客户端生成 PDF 示例</h3>  <script src="https://unpkg.com/jspdf@latest/dist/jspdf.min.js"></script>  <script>  (function generatePdf() {  const doc = new jsPDF();  doc.text("Hello semlinker!", 66, 88);  const blob = new Blob([doc.output()], { type: "application/pdf" });  blob.text().then((blobAsText) => {  console.log(blobAsText);  });  })();  </script>  </body> 复制代码

其实 jsPDF 除了支持纯文本以外,它也能够生成带图片的 PDF 文档,好比:

let imgData = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/...'
let doc = new jsPDF();  doc.setFontSize(40); doc.text(35, 25, 'Paranyan loves jsPDF'); doc.addImage(imgData, 'JPEG', 15, 40, 180, 160); 复制代码

4、Blob 与 ArrayBuffer 有何区别?

1. 定义区别

ArrayBuffer 对象用于表示通用的,固定长度的原始二进制数据缓冲区。且不能直接操纵 ArrayBuffer 的内容,须要建立一个类型化数组对象或 DataView 对象,该对象以特定格式表示缓冲区,并使用该对象读取和写入缓冲区的内容。

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

Blob 类型只有 slice 方法,用于返回一个新的 Blob 对象,包含了源 Blob 对象中指定范围内的数据。 对比发现,ArrayBuffer 的数据,是能够按照字节去操做的,而 Blob 只能做为一个完整对象去处理。因此说,ArrayBuffer 相比 Blob 更接近真实的二进制,更底层。

2. 二者互转

2.1 ArrayBuffer 转 Blob

只需将 ArrayBuffer 做为参数传入便可:

const buffer = new ArrayBuffer(16);
const blob = new Blob([buffer]); 复制代码

2.2 Blob 转 ArrayBuffer

须要借助 FileReader 对象:

const blob = new Blob([1,2,3,4,5]);
const reader = new FileReader();  reader.onload = function() {  console.log(this.result); } reader.readAsArrayBuffer(blob); 复制代码

3. 其余区别

  1. 须要使用写入/编辑操做时使用 ArrayBuffer,不然使用 Blob 便可;
  2. Blob 对象不可变,而 ArrayBuffer 能够经过 TypedArrays 或 DataView 操做;
  3. Blob 能够位于磁盘、高速缓存内存和其余不一样用位置,而 ArrayBuffer 存在内存中,能够直接操做;

4. Ajax 中使用 Blob 和 ArrayBuffer

function GET(url, callback) {
 let xhr = new XMLHttpRequest();  xhr.open('GET', url, true);  xhr.responseType = 'arraybuffer'; // or xhr.responseType = "blob";  xhr.send();   xhr.onload = function(e) {  if (xhr.status != 200) {  alert("Unexpected status code " + xhr.status + " for " + url);  return false;  }  callback(new Uint8Array(xhr.response)); // or new Blob([xhr.response]);  }; } 复制代码

5、拓展

1. Blob URL 和 Data URL 区别

1.1 格式不一样

Blob URL 格式如 blob:域名/uuidData URL 格式如: data:[<mediatype>][;base64],<data>  。
mediatype 是个 MIME 类型的字符串,例如 "image/jpeg" 表示 JPEG 图像文件。若是被省略,则默认值为 text/plain;charset=US-ASCII
image.png

1.2 长度不一样

Blob URL 通常长度较短,而 Data URL 由于直接存储图片 base64 编码后的数据,每每比较长。

1.3 XMLHttpRequest 支持状况不一样

Blob URL  能够很方便使用 XMLHttpRequest 获取源数据( xhr.responseType = 'blob' ),而 Data URL 并非全部浏览器都支持经过 XMLHttpRequest 获取源数据的。

1.4 使用场景不一样

Blob URL  只能在当前应用内使用,把 Blob URL  复制到浏览器地址栏是没法获取数据,而 Data URL 则能够在任意浏览器中使用。

6、总结

本文中咱们主要经过 4 个问题来复习了 Blob 知识点:“Blob 是什么”、“Blob 怎么用”、“Blob 使用场景”和“Blob 与 ArrayBuffer 区别”,在“Blob 使用场景”部分中,也主要介绍了咱们实际开发中很是常见的“图片预览”、“图片下载”和“生成文件”的场景。在文章最后,也经过和你们复习了“Blob URL 和 Data URL 区别”,让咱们对 Blob 有更深的认识。

本文使用 mdnice 排版

相关文章
相关标签/搜索