添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

在开发中,相信很多人都做过图片上传相关功能。然而,在图片上传过程中,文件体积问题常常被忽视。若直接上传原图片,可能因体积较大导致上传速度慢,且前端渲染时也会比较迟缓,极大地影响用户体验。因此,在不影响清晰度的前提下,前端可以在上传前对图片大小体积进行压缩,调整到合适大小后再进行上传。本文将详细介绍前端 JS 如何实现图片压缩,有需要的小伙伴赶紧收藏起来吧!

原理(必看)

简而言之:主要利用 canvas 的 drawImage 方法先将图片绘制为 canvas 图像,再通过 toDataURL 转化为 DataURL 来存储图片链接。

drawImage 简单介绍

Canvas 2D API 中的 CanvasRenderingContext2D.drawImage () 方法提供了多种在画布(Canvas)上绘制图像的方式。

用法可参考:CanvasRenderingContext2D.drawImage () - Web API 接口参考 | MDN (mozilla.org)。

语法如下:

drawImage (image, dx, dy);

drawImage (image, dx, dy, dWidth, dHeight);

drawImage (image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);

我们使用第二种语法进行绘制,参数含义如下:

image:要绘制到上下文的元素。

dx:image 的左上角在目标画布上的 X 轴坐标。

dy:image 的左上角在目标画布上的 Y 轴坐标。

dWidth:image 在目标画布上绘制的宽度,可对绘制的 image 进行缩放。若不指定,绘制时 image 宽度不会缩放。

dHeight:image 在目标画布上绘制的高度,可对绘制的 image 进行缩放。若不指定,绘制时 image 高度不会缩放。

简单示例

注意:随意修改图像尺寸可能导致图像失真。我们可以先获取图像资源的原始尺寸,然后进行等比缩放。即当确定设置宽度后,高度要进行等比调整,可依据交叉相乘积相等的公式进行计算。

例如,如果宽度设置为 500,那么高度也应进行等比缩放。公式为:naturalWidth * X = naturalHeight * 500,由此可计算出高度 X = naturalHeight * 500 /naturalWidth。

以下是代码示例:

javascript

复制

var can = document.querySelector('canvas');

var context = can.getContext('2d');

var imgDom = new Image();

imgDom.src = './img.jpg';

imgDom.onload = function () {

// 注意:图像绘制时,必须保证资源已经加载完成

console.log('图片的原始宽度', imgDom.naturalWidth);

console.log('图片的原始高度', imgDom.naturalHeight);


context.drawImage(

imgDom,

0, 0,

500, imgDom.naturalHeight * 500 / imgDom.naturalWidth

);

};

toDataURL 简单介绍

将图片绘制到 canvas 后,还需将 canvas 转化为 Data URL。转化为 DataURL 后,可以在屏幕上显示,也可存储到后端服务器。使用 canvas 提供的 toDataURL 实例方法即可。

官方解释:HTMLCanvasElement.toDataURL () 方法返回一个包含图片展示的 data URI。

参考:HTMLCanvasElement.toDataURL () - Web API 接口参考 | MDN (mozilla.org)。

语法:canvas.toDataURL (type, encoderOptions);

其中,type(可选)为图片格式,默认为 image/png;encoderOptions(可选)在指定图片格式为 image/jpeg 或 image/webp 的情况下,可以从 0 到 1 的区间内选择图片的质量。若超出取值范围,将使用默认值 0.92,其他参数会被忽略。

简单示例

javascript

复制

// 获取压缩后的图片数据

can.width = imgDom.naturalWidth;

can.height = imgDom.naturalHeight;


const compressedData = can.toDataURL('image/jpeg', 0.6); // 可调整质量参数

console.log('compressedData: ', compressedData);

转化后的 DataURL 结果如下。

实现

先给出全部代码,再进行解释。

html

复制

<!DOCTYPE html>

<html>

<head>

<title>图片压缩上传</title>

<meta charset="UTF-8">

</head>

<body>

<input type="file" id="fileInput" accept="image/*">

<button onclick="compressAndUpload()">压缩并上传图片</button>

<canvas id="canvas" style="display: none;"></canvas>

<script>

function compressAndUpload() {

const fileInput = document.getElementById('fileInput');

const file = fileInput.files[0];

if (!file) {

alert('请先选择要上传的图片');

return;

}

const reader = new FileReader();

reader.onload = function () {

const img = new Image();

img.src = reader.result;

img.onload = function () {

const canvas = document.getElementById('canvas');

const ctx = canvas.getContext('2d');

const maxWidth = 800; // 设置最大宽度为 800 像素

let width = img.width;

let height = img.height;


// 判断是否需要缩放

if (width > maxWidth) {

height *= maxWidth / width;

width = maxWidth;

}

// 设置 canvas 的宽高

canvas.width = width;

canvas.height = height;


// 将图片绘制到 canvas 上

ctx.drawImage(img, 0, 0, width, height);

// 获取压缩后的图片数据

const compressedData = canvas.toDataURL('image/jpeg', 0.7); // 可调整质量参数


// 创建一个新的压缩后的 File 对象

const compressedFile = dataURItoBlob(compressedData, file.type);

compressedFile.lastModifiedDate = file.lastModifiedDate;

compressedFile.name = file.name;


// 上传压缩后的图片文件

uploadImage(compressedFile);

};

};

reader.readAsDataURL(file);

}


function dataURItoBlob(dataURI, mimeType) {

const binary = atob(dataURI.split(',')[1]);

const array = [];

for (let i = 0; i < binary.length; i++) {

array.push(binary.charCodeAt(i));

}

return new Blob([new Uint8Array(array)], { type: mimeType });

}


function uploadImage(compressedFile) {

const formData = new FormData();

formData.append('image', compressedFile);


fetch('/upload', {

method: 'POST',

body: formData

})

.then(response => {

if (response.ok) {

console.log('图片上传成功');

} else {

console.error('图片上传失败');

}

})

.catch(error => {

console.error('发生错误:', error);

});

}

</script>

</body>

</html>

下面分析关键代码部分。

首先,初始化一个 reader,它是 FileReader 对象的实例。reader.readAsDataURL(file)这行代码将选择的文件读取为 Data URI 格式的字符串。执行这行代码时,FileReader 对象会异步读取文件数据,读取完成后触发 onload 事件,读取结果存储在 FileReader 对象的 result 属性中,格式为 Data URI 字符串。

接着,将读取出来的 Data URI 字符串赋值给 Image 的 src,等待 img 加载完毕后开始对 img 进行压缩,压缩方法如上文所示。

然后,设置最大宽度为 800,判断当前图片宽度是否大于该值,若大于则进行缩放计算,小于则不进行等比缩放计算。最后将计算出的值使用 drawImage 绘制到 canvas 上面。

现在,将 canvas 转化成 Data URI 字符串,canvas.toDataURL('image/jpeg', 0.7)这行代码将 canvas 上绘制的图像数据导出为 JPEG 格式的 Data URI 字符串,并设置图像质量为 0.7。

之后,创建一个新的压缩后的 File 对象,通过将 Data URI 字符串转化为 Blob 对象来实现。

最后,使用 FormData 进行上传即可。

压缩前后体积对比

我们看一下压缩前后体积对比,压缩前为 550290,压缩后为 31523,缩小了十几倍,压缩效果非常明显。

总结

前端实现图片压缩主要借助 canvas 来完成。实现思路是先使用 canvas 的 drawImage 方法将图片绘制为 canvas 图像,再结合 toDataURL 转化为 DataURL 进行存储图片链接以及压缩图像质量。在 toDataURL 中可以调整图像质量,同时,在压缩图像时要注意等宽高缩放,否则会导致图像失真。