《你不知道的 Blob》番外篇 下

简介: 《你不知道的 Blob》番外篇 下

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() 方法来实现,该方法接收 type 和 encoderOptions 两个可选参数:

  • type 表示图片格式,默认为 image/png ;
  • encoderOptions 表示图片质量,在指定图片格式为 image/jpeg 或 image/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);

四、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]);
  };
}

五、拓展

1. Blob URL 和 Data URL 区别

1.1 格式不同

Blob URL 格式如 blob:域名/uuid , Data URL 格式如: data:[<mediatype>][;base64],<data>  。

mediatype 是个 MIME 类型的字符串,例如 "image/jpeg" 表示 JPEG 图像文件。如果被省略,则默认值为 text/plain;charset=US-ASCII。

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 则可以在任意浏览器中使用。

六、总结

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

本文使用 mdnice 排版

目录
相关文章
|
存储 JavaScript 前端开发
vue3 专用 indexedDB 封装库,基于Promise告别回调地狱(二)
https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API 这个大概是官网吧,原始是英文的,现在陆续是出中文版。有空的话还是多看看官网。
|
算法 数据可视化 API
常用的Python第三方库中哪个库可以用于图像处理?
常用的Python第三方库中哪个库可以用于图像处理?
788 5
|
文字识别 计算机视觉 Python
我用 Python 写了一个自动裁剪答题卡区域的小工具(附代码)
本文分享了一种通过 OpenCV 自动裁剪答题卡中答题区域的方法。核心思路是利用答题区域四周的黑色角块进行定位:先通过自适应阈值增强对比度,再用 `cv2.findContours()` 找轮廓,并计算每个轮廓的“紧凑度”(面积 / 周长)筛选出接近方块的角块。最终根据四个角块的边界矩形裁剪出答题区。代码实现详细,适合初学者参考,同时提供了参数调整建议以适配不同图像条件。
658 10
|
人工智能 JSON 前端开发
ChatGPT如何实现聊天一样的实时交互?快速读懂SSE实时“推”技术
本文将带你快速认识SSE实时通信协议,包括它的技术原理、常见使用场景、与同类技术的对比以及简单的示例代码等。
484 0
ChatGPT如何实现聊天一样的实时交互?快速读懂SSE实时“推”技术
|
JavaScript 网络协议 Java
理解websocket实现原理
WebSocket是实现高效双向实时通信的协议,通过握手阶段和特定数据帧格式突破HTTP限制,降低延迟。在项目中使用WebSocket,通常涉及选择库(如`ws`、`Java-WebSocket`、`Socket.IO`),创建服务器监听连接和处理消息,以及在客户端建立连接并收发消息。WebSocket适用于聊天、游戏、协同编辑等场景。示例代码展示了使用Node.js和`ws`库创建WebSocket服务器的基本步骤。
1414 95
|
调度 数据库 Python
python中APScheduler的使用详解(python3经典编程案例)
文章详细讲解了在Python中使用APScheduler来安排和执行定时任务的方法,包括不同调度器的配置与使用场景。
1171 1
|
前端开发 JavaScript 安全
集成WebSocket在Spring Boot中可以用于实现实时的双向通信
集成WebSocket在Spring Boot中可以用于实现实时的双向通信
929 4
|
存储 前端开发 网络架构
vue3-admin-element框架实现动态路由(根据接口返回)
vue3-admin-element框架实现动态路由(根据接口返回)
833 1
|
存储 Linux 调度
高效定时任务处理:深入学习Python中APScheduler库的奥秘
高效定时任务处理:深入学习Python中APScheduler库的奥秘
17855 7
高效定时任务处理:深入学习Python中APScheduler库的奥秘
|
存储 开发框架 算法

热门文章

最新文章