一、简介
最近项目需要对上传的图片文件进行压缩后才上传到服务器中,于是研究了一番,下面给出详细的压缩方法,笔者使用的是React Ant Design前端框架的Upload组件上传图片:
通过查看Ant Design官网文档,在上传文件前可以修改文件:
transformFile | 在上传之前转换文件。支持返回一个 Promise 对象 | Function(file): string | Blob | File | Promise<string | Blob | File> |
无 |
二、压缩相关代码
图片压缩的原理:实际上根据图片大小有没有超过预定的最大最小时,如果超过指定的高度/宽度,在不怎么失真的前提下裁剪图片,然后使用canvas画布的drawImage()方法绘制图片。下面是关键的代码:
//在上传之前转换文件
transformFile = (file) => {
/**
* 针对图片进行压缩,如果图片大小超过压缩阈值,则执行压缩,否则不压缩
*/
//判断是否是图片类型
if (this.checkIsImage(file.name)) {
const {compressThreshold = 5, isPictureCompress = false, pictureQuality = 0.92} = this.props;
let fileSize = file.size / 1024 / 1024;
// console.log('before compress, the file size is : ', fileSize + "M");
//当开启图片压缩且图片大小大于等于压缩阈值,进行压缩
if ((fileSize >= compressThreshold) && isPictureCompress) {
//判断浏览器内核是否支持base64图片压缩
if (typeof (FileReader) === 'undefined') {
return file;
} else {
try {
this.setState({
spinLoading: true
});
return new Promise(resolve => {
//声明FileReader文件读取对象
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
// 生成canvas画布
const canvas = document.createElement('canvas');
// 生成img
const img = document.createElement('img');
img.src = reader.result;
img.onload = () => {
const ctx = canvas.getContext('2d');
//原始图片宽度、高度
let originImageWidth = img.width, originImageHeight = img.height;
//默认最大尺度的尺寸限制在(1920 * 1080)
let maxWidth = 1920, maxHeight = 1080, ratio = maxWidth / maxHeight;
//目标尺寸
let targetWidth = originImageWidth, targetHeight = originImageHeight;
//当图片的宽度或者高度大于指定的最大宽度或者最大高度时,进行缩放图片
if (originImageWidth > maxWidth || originImageHeight > maxHeight) {
//超过最大宽高比例
if ((originImageWidth / originImageHeight) > ratio) {
//宽度取最大宽度值maxWidth,缩放高度
targetWidth = maxWidth;
targetHeight = Math.round(maxWidth * (originImageHeight / originImageWidth));
} else {
//高度取最大高度值maxHeight,缩放宽度
targetHeight = maxHeight;
targetWidth = Math.round(maxHeight * (originImageWidth / originImageHeight));
}
}
// canvas对图片进行缩放
canvas.width = targetWidth;
canvas.height = targetHeight;
// 清除画布
ctx.clearRect(0, 0, targetWidth, targetHeight);
// 绘制图片
ctx.drawImage(img, 0, 0, targetWidth, targetHeight);
// quality值越小,图像越模糊,默认图片质量为0.92
const imageDataURL = canvas.toDataURL(file.type || 'image/jpeg', pictureQuality);
// 去掉URL的头,并转换为byte
const imageBytes = window.atob(imageDataURL.split(',')[1]);
// 处理异常,将ascii码小于0的转换为大于0
const arrayBuffer = new ArrayBuffer(imageBytes.length);
const uint8Array = new Uint8Array(arrayBuffer);
for (let i = 0; i < imageBytes.length; i++) {
uint8Array[i] = imageBytes.charCodeAt(i);
}
let mimeType = imageDataURL.split(',')[0].match(/:(.*?);/)[1];
let newFile = new File([uint8Array], file.name, {type: mimeType || 'image/jpeg'});
// console.log('after compress, the file size is : ', (newFile.size / 1024 / 1024) + "M");
resolve(newFile);
};
};
reader.onerror = () => {
this.setState({
spinLoading: false
});
return file;
}
}).then(res => {
this.setState({
spinLoading: false
});
return res;
}).catch(() => {
this.setState({
spinLoading: false
});
return file;
});
} catch (e) {
this.setState({
spinLoading: false
});
//压缩出错,直接返回原file对象
return file;
}
}
} else {
//不需要压缩,直接返回原file对象
return file;
}
} else {
//非图片文件,不进行压缩,直接返回原file对象
return file;
}
};
//检查是否为图片类型
checkIsImage = (fileName) => {
let flag;
let imageTypeList = ['.jpg', '.png', '.jpeg', '.gif', '.GIF', '.JPG', '.PNG', '.JPEG'];
for (let item of imageTypeList) {
flag = fileName.indexOf(item) !== -1;
if (flag) {
return true;
}
}
return flag;
};
相关属性说明:
-
compressThreshold: 5, //压缩的阈值,图片大小超过5M,则需要进行压缩 isPictureCompress: false, //是否开启图片压缩 pictureQuality: 0.92, //指定压缩的图片质量,取值范围为0~1,quality值越小,图像越模糊,默认图片质量为0.92
三、使用方法
<NHUpload
uploadType={'file'}
multiple={true}
fileCountLimit={fjsl}
maxFileSize={20}
fileTypeLimit={fileTypeList}
onChange={this.fjOnChange}
isPictureCompress={true} //是否开启图片压缩
pictureQuality={0.5} //图片质量
compressThreshold={1} //压缩阈值
/>
在使用时,我们可以根据业务需求动态设置需要压缩的阈值,图片质量等等,对图片压缩可以大大节省服务器的资源,现在手机随便拍一张照片就是10几兆。