前端二进制相关知识点总结之图片篇(二)


前言

再了解了 Blob、Blob URL、Base64、Data URL、ArrayBuffer、TypedArray、DataView 之间的关系后,我们再来了解一下关于图片灰度化,压缩 和 上传 的知识点。


一、图片灰度化

要对图片进行灰度化处理,就需要操作图片像素数据。那么问题来了,我们应该如何获取图片的像素数据呢?

1. getImageData 方法

针对上述问题,我们可以利用 CanvasRenderingContext2D 提供的 getImageData 来获取图片像素数据;其中 getImageData() 返回一个 ImageData 对象,用来描述 canvas 区域隐含的 像素数据 ,这个区域通过矩形表示,起始点为 (sx, sy) 、宽为 sw 、高为 sh

语法:

ctx.getImageData(sx, sy, sw, sh);

参数说明:

  • sx :将要被提取的图像数据矩形区域的左上角 x 坐标;
  • sy :将要被提取的图像数据矩形区域的左上角 y 坐标;
  • sw :将要被提取的图像数据矩形区域的宽度;
  • sh :将要被提取的图像数据矩形区域的高度。

2. putImageData 方法

在获取到图片的像素数据之后,就可以对获取的像素数据进行处理,比如进行灰度化或反色处理。当完成处理后,若要在页面上显示处理效果,则我们需要利用 CanvasRenderingContext2D 提供的另一个 API —— putImageData
该 API 是 Canvas 2D API 将数据从已有的 ImageData 对象绘制到位图的方法。

语法:

ctx.putImageData(imagedata, dx, dy);
ctx.putImageData(imagedata, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight);

参数说明:

  • imageData : ImageData ,包含像素值的数组对象;
  • dx :源图像数据在目标画布中的 x 轴方向的偏移量;
  • dy :源图像数据在目标画布中的 y 轴方向的偏移量;
  • dirtyX :可选,在源图像数据中,矩形区域左上角的位置。默认是整个图像数据的左上角(x 坐标)。
  • dirtyY :可选,在源图像数据中,矩形区域左上角的位置。默认是整个图像数据的左上角(y 坐标)。
  • dirtyWidth :可选,在源图像数据中,矩形区域的宽度。默认是图像数据的宽度。
  • dirtyHeight :可选,在源图像数据中,矩形区域的高度。默认是图像数据的高度。

3. 图片灰度化处理

利用 getImageData()putImageData() 方法, 实现图片灰度化:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>获取远程图片并灰度化</title>
  </head>
  <body>
  	<h3>获取远程图片并灰度化示例</h3>
    <div>
      <button id="grayscalebtn">灰度化</button>
      <div style="display: flex;">
        <div style="flex: 50%;">
          <p>预览容器</p>
          <img
            id="previewContainer"
            width="230"
            height="230"
            style="border: 2px dashed blue;"
          />
        </div>
        <div style="flex: 50%;">
          <p>Canvas容器</p>
          <canvas
            id="canvas"
            width="230"
            height="230"
            style="border: 2px dashed grey;"
          ></canvas>
        </div>
      </div>
    </div>
    <script>
      const image = document.querySelector("#previewContainer");
      const canvas = document.querySelector("#canvas");
	  // 远程图片预览
      fetch("https://s.yimg.com/ut/api/res/1.2/wc8Y6ZMRPK6k0ucegJ2J5Q--~B/YXBwaWQ9eXR3bWFsbDtjYz0zMTUzNjAwMDtoPTYwMDtxPTEwMDt3PTYwMA--/https://s.yimg.com/fy/8236/item/p0265120751355-item-ebbbxf4x0800x0800-m.jpg")
        .then((response) => response.blob())
        .then((blob) => {
          const objectURL = URL.createObjectURL(blob);
          image.src = objectURL;
          image.onload = () => {
            draw();
          };
        });
	  // 把获取的图像绘制到页面的 Canvas 容器中
      function draw() {
        const ctx = canvas.getContext("2d");
        ctx.drawImage(image, 0, 0, 230, 230);
        // 通过 ctx.getImageData() 方法获取的图片,便于在grayscale函数中 图片像素进行灰度化处理
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const data = imageData.data;

		// 图片灰度化
        const grayscale = function () {
          for (let i = 0; i < data.length; i += 4) {
            const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
            data[i] = avg; // red
            data[i + 1] = avg; // green
            data[i + 2] = avg; // blue
          }
          // 处理完成后,通过 ctx.putImageData() 方法把处理过的像素数据更新到 Canvas 上
          ctx.putImageData(imageData, 0, 0);
        };

		// 按钮绑定监听事件
        const grayscalebtn = document.querySelector("#grayscalebtn");
        grayscalebtn.addEventListener("click", grayscale);
      }
    </script>
  </body>
</html>

页面初始化效果:
在这里插入图片描述

点击 “灰度化”按钮后效果:
在这里插入图片描述
ImageData:

在这里插入图片描述

二、图片压缩

在一些场合中,我们希望在上传本地图片时,先对图片进行一定的压缩,然后再提交到服务器,从而减少传输的数据量。在前端要实现图片压缩,我们可以利用 Canvas 对象提供的 toDataURL() 方法,该方法接收 typeencoderOptions 两个可选参数。

语法:

canvas.toDataURL(type, encoderOptions);

参数说明:

  • type: 表示图片格式,默认为 image/png
  • encoderOptions :表示图片的质量,在指定图片格式为 image/jpegimage/webp 的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92,其他参数会被忽略。

示例:

/* 添加压缩后的图片预览容器 */
<div style="flex: 33.3%;">
	<p>压缩预览容器</p>
    <img id="compressPrevContainer" width="230" height="230" style="border: 2px dashed green;" />
</div>
const compressbtn = document.querySelector("#compressbtn");
const compressImage = document.querySelector("#compressPrevContainer");
compressbtn.addEventListener("click", compress); 

// 图片压缩
function compress(quality = 80, mimeType = "image/webp") {
	const imageDataURL = canvas.toDataURL(mimeType, quality / 100);
	compressImage.src = imageDataURL;
}

在以上代码中,我们设默认的图片质量是 0.8 ,而图片类型是 image/webp 类型。当用户点击压缩按钮时,则会调用 Canvas 对象的 toDataURL() 方法实现图片压缩。

最终的处理效果:

在这里插入图片描述
其实 Canvas 对象除了提供 toDataURL() 方法之外,它还提供了一个 toBlob() 方法。

语法:

canvas.toBlob(callback, mimeType, qualityArgument)

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

三、图片上传

在获取压缩后图片对应的 Data URL 数据之后,可以把该数据直接提交到服务器。针对这种情形,服务端需要做一些相关处理,才能正常保存上传的图片。

这里以 Express 为例,具体处理代码如下:

const app = require('express')();

app.post('/upload', function(req, res){
	// 获取POST请求中的base64图片数据
    let imgData = req.body.imgData; 
    let base64Data = imgData.replace(/^data:image\/\w+;base64,/, "");
    let dataBuffer = Buffer.from(base64Data, 'base64');
    fs.writeFile("melod.jpg", dataBuffer, function(err) {
        if(err) {
          res.send(err);
        }else {
          res.send("图片上传成功!");
        }
    });
});

然而对于返回的 Data URL 格式的图片数据一般都会比较大,为了进一步减少传输的数据量,我们可以把它转换为 Blob 对象:

function dataUrlToBlob(base64, mimeType) {
  let bytes = window.atob(base64.split(",")[1]);
  let buffer = new ArrayBuffer(bytes.length);
  let uin8 = new Uint8Array(buffer);
  for (let i = 0; i < bytes.length; i++) {
    uin8[i] = bytes.charCodeAt(i);
  }
  return new Blob([buffer], { type: mimeType });
}

在转换完成后,我们就可以压缩后的图片对应的 Blob 对象封装在 FormData 对象中,然后再通过 AJAX 提交到服务器上:

function uploadFile(url, blob) {
  let formData = new FormData();
  let request = new XMLHttpRequest();
  formData.append("imgData", blob);
  request.open("POST", url, true);
  request.send(formData);
}

四、图片相关的知识点总结

1. 如何区分图片的类型

计算机并不是通过图片的后缀名来区分不同的图片类型,而是通过 “魔数”(Magic Number)来区分。 对于某一些类型的文件,起始的几个字节内容都是固定的,根据这几个字节的内容就可以判断文件的类型。

常见图片类型对应的魔数如下表所示:

文件类型文件后缀魔数
JPEGjpg/jpeg0xFFD8FF
PNGpng0x89504E47
GIFgif0x47494638(GIF8)
BMPbmp0x424D

在日常开发过程中,如果遇到检测图片类型的场景,可以直接利用一些现成的第三方库。比如,想要判断一张图片是否为 PNG 类型,这时你可以使用 is-png 这个库,它同时支持浏览器和 Node.js。

2. 如何获取图片的尺寸

图片的尺寸、位深度、色彩类型和压缩算法都会存储在文件的二进制数据中。
如果想要获取图片的尺寸,就需要依据不同的图片格式对图片二进制数据进行解析。可以通过 image-size 这个 Node.js 库实现了获取主流图片类型文件尺寸的功能。

3. 如何实现大文件分片上传

File 对象是特殊类型的 Blob,且可以用在任意的 Blob 类型的上下文中。所以针对大文件传输的场景,我们可以使用 slice 方法对大文件进行切割,然后分片进行上传,具体示例如下:

const file = new File(["a".repeat(1000000)], "test.txt");

const chunkSize = 40000;
const url = "https://httpbin.org/post";

async function chunkedUpload() {
  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) =>
        res.text()
      );
  }
}

4. 如何实现文件下载

在一些场景中,我们会通过 Canvas 进行图片编辑或使用 jsPDFsheetjs 等一些第三方库进行文档处理,当文件文件处理完成后,我们需要把文件下载并保存到本地。针对这些场景,我们可以使用纯前端的方案实现文件下载。

一个简单的 Blob 文件下载的示例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Blob 文件下载示例</title>
  </head>

  <body>
    <button id="downloadBtn">文件下载</button>
    <script>
    	// 下载
    	const download = (fileName, blob) => {
    		// 动态创建 a 标签
  			const link = document.createElement("a");
  			link.href = URL.createObjectURL(blob);
  			link.download = fileName;
  			link.click();
  			link.remove();
  			URL.revokeObjectURL(link.href);
		};

		const downloadBtn = document.querySelector("#downloadBtn");
		downloadBtn.addEventListener("click", (event) => {
			// 对下载文件命名
  			const fileName = "blob.txt";
  			// 创建下载对象,类型为 text/plain
  			const myBlob = new Blob(["我是内容我是内容我是内容"], { type: "text/plain" });
  			download(fileName, myBlob);
		});
	</script>
  </body>
</html>

页面效果:
在这里插入图片描述

在示例中,通过调用 Blob 的构造函数来创建类型为 “text/plain” 的 Blob 对象,然后通过动态创建 a 标签来实现文件的下载。在实际项目开发过程中,我们可以使用成熟的开源库,比如 FileSaver.js 来实现文件保存功能。


总结

在这里插入图片描述

在这里插入图片描述
博客转载自: 玩转前端二进制链接

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值