基于HTML5的Canvas技术实现前端的图片压缩

Canvas应用:图片压缩算法实现

前端图片压缩应用场景

  1. 前端页面限制用户只可以上传5MB大小的图片
  2. 前端在接受到用户上传的图片之后,服务器只允许上传1MB大小的图片,此时需要前端将图片先进行压缩,压缩之后再调用图片上传接口将图片上传

第一步:创建input元素,监听input表单域的change事件

  1. 创建一个input元素,并且设置HTML5新增的type为file
  2. 监听input元素的change事件,并通过e.target.files拿到用户上传的文件列表数组
  3. 获取用户上传的文件对象,校验其是否为空
  4. 校验文件的type类型是否为图片类型
  5. 校验文件的size大小是否超出5MB
  6. 如果校验通过,那么才开始进行图片压缩
<input type="file" id="upload"></input>
<script type="text/javascript">
	const upload = document.getElementById('#upload');
	const acceptType = ['image/png','image/jpeg','image/jpg'];
	const maxSize = 3 * 1024 *1024;
	
	upload.addEventListener('change',function(e){
		let file = e.target.files[0];
		if(!file)return;
		let {type:fileType,size:fileSize} = file;
		if(!acceptType.includes(fileType)){
			alert(`不支持${fileType}类型文件`);
			upload.value = '';
			return;
		}
		if(fileSize > maxSize){
			alert(`文件大小超出3MB!`);
			upload.value = '';
			return;
		}
		convertImageToBase64(file,function(base64Image){
			compress(base64Image,uploadToServer);
		});
	})

</script>

第二步:图片压缩第一步先将用户上传图片转化为base64格式字符串

  1. 创建一个浏览器内置的FileReader对象的实例reader
  2. 调用reader.readAsDataURL()方法,参数接收一个blob对象或者file对象
  3. 调用该方法之后会开始读取传入的文件file,读取完成后会触发load完成事件
  4. 在load事件中,通过e.target.result或者reader.result获取file对应的base64格式字符串
  5. 执行传入的callback函数,此时开始执行compress压缩函数
  6. 将reader指向null,防止内存泄漏
function convertImageToBase64(file,callback){
	const reader = new FileReader();
	reader.addEventListener('load',function(e){
		let base64Image = e.target.result;
		callback && callback(base64Image);
		reader = null;
	})
	reader.readAsDataURL(file);
}

第三步:执行compress方法,开始压缩图片

  1. 创建一个image对象,为了方便获取用户上传图片的原始宽高
  2. 设置最大宽度和高度
  3. 将image的src属性设置为base64Image,并监听image的load事件
  4. 当load事件触发后,在事件回调函数中进行如下逻辑:
  5. 设置压缩比ratio
  6. 设置是否需要压缩的变量needCompress
  7. 校验原始宽度超出宽度,计算压缩比,如果超出,此时最大高度需要等比例变化
  8. 校验原始高度超出高度,计算压缩比,如果超出,此时最大宽度需要等比例变化
  9. 如果原始宽高都没有超出,那么需要将最大宽高的值修改为原始宽高
  10. 动态创建一个canvas元素
  11. 设置canvas元素的宽高属性分别为最大宽度和高度后插入到页面中
  12. 设置canvas的visibility属性为hidden或者visibile,看具体项目需求
  13. 获取ctx 2d上下文对象
  14. 清空上一次绘图的画布ctx.clearRect(0,0,最大宽度,最大高度)
  15. 绘制图片ctx.drawImage(image,0,0,最大宽度,最大高度)绘制出图片,此时已经将图片的尺寸进行了压缩
  16. 输出图片canvas.toDataURL('image/jpeg',0.8);前者是输出格式,后者是输出质量。选jpeg本来就会对图片进行压缩,输出质量也会进行压缩。
  17. 拿到上一步输出的base64格式的图片地址,将其作为参数传递给callback,compress函数中的callb18. 通过对比image.src.length可以计算压缩前后的压缩比
function compress(base64Image,callback){
	const image = new Image();
	let maxW = 1024,
		maxH = 1024;

	image.addEventListener('load',function(e){
		const needCompress = false,
			  ratio = null;
		if(image.naturalWidth > maxW){
		   needCompress = true;
		   ratio = naturalWidth / maxW;
		   maxH = naturalHeight/ratio;
		}
		if(image.naturalHeight > maxH){
		    needCompress = true;
		    ratio = naturalHeight / maxH;
		    maxW = naturalWidth/ratio;
		}
		if(!needCompress){
			 maxW = naturalWidth;
			 maxH = naturalHeight;
		}
		
		let canvas = document.createElement('canvas');
		canvas.width = maxW;
		canvas.height = maxH;
		document.body.appendChild(canvas);
		canvas.style.visibility = 'hidden';
		let ctx = canvas.getContext('2d');
		ctx.clearRect(0,0,maxW,maxH);
		ctx.drawImage(image,0,0,maxW,maxH);image是要生成的image对象
		let compressedBase64Image = canvas.toDataURL('image/jpeg',0.8);
		canvas.remove();
		
		callback && callback(compressedBase64Image);
		const _ratio = base64Image.length / compressedBase64Image.length;
		console.log('图片压缩比为:',_ratio);
		
	})
	
	image.src = base64Image;
}

第四步:将图片上传至服务器

  1. 接收传递的压缩后图片的base64字符串compressImage
  2. 将其上传至服务器
function uploadToServer(compressedBase64Image){
	console.log('上传的图片为:' + compressedBase64Image);
	axios.post({
		url:'192.168.01.02/api/upload',
		data:{
			
		},
		headers:{
			'Content-Type':'multipart/form-data'
		}
	})
}

合并后的代码如下:

<input type="file"  id="upload"/>
function convertImageToBase64(file,callback){
	// 实例化FileReader对象,主要用于读取文件内容
	let reader = new FileReader();
	// 监听文件加载完成的事件
	reader.addEventListener('load',function(e){
		// 直接用reader实例的result属性拿到base64格式
		// console.log(reader.result);
		
		// e.target.result获取到文件的base64格式
		// console.dir(e.target.result);
		let base64Image = e.target.result;
		// 执行callback()
		callback && callback(base64Image);
		// 将reader指向null防止内存泄漏
		reader = null;
	})
	// 调用实例的readAsDataURL方法
	reader.readAsDataURL(file);
}

function uploadToServer(compressedBase64Image){
	console.log('上传的图片为:' + compressedBase64Image);
	axios.post({
		url:'192.168.01.02/api/upload',
		data:{
			
		},
		headers:{
			'Content-Type':'multipart/form-data'
		}
	})
}

function compress(base64Image,callback){
	const image = new Image(); 
	let maxWidth = 1024;
	let maxHeight = 1024;
	
	image.addEventListener('load',function(e){
		let ratio;
		let needCompress = false;
		if(image.naturalWidth > maxWidth){
			needCompress = true;
			ratio = image.naturalWidth / maxWidth;
			maxHeight = image.naturalHeight / ratio;
		}
		if(image.naturalHeight > maxHeight){
			needCompress = true;
			ratio = image.naturalHeight / maxHeight;
			maxWidth = image.naturalWidth / ratio;
		}
		// 如果不需要压缩 那么需要获取图片的实际尺寸
		if(!needCompress){
			maxWidth = image.naturalWidth;
			maxHeight = image.naturalHeight;
		}
		
		// 创建一个画步  画布大小 就是压缩尺寸后的图片大小
		const canvas = document.createElement('canvas');
		canvas.width = maxWidth;
		canvas.height = maxHeight;
		
		let ctx = canvas.getContext('2d');
		ctx.clearRect(0,0,maxWidth,maxHeight);
		ctx.drawImage(image,0,0,maxWidth,maxHeight);
		const compressImage = canvas.toDataURL('image/jpeg',0.8);
		canvas.remove();
		
		// 此时已经拿到压缩后图片的base64格式 下面要将图片上传到服务器
		callback && callback(compressImage);
		
		
		const _image = new Image();
		_image.src = compressImage;
		_image.id = 'chooseImage';
		document.body.appendChild(_image);
		
		
		console.log(base64Image.length/compressImage.length);压缩比
	})
	image.src = base64Image;
}

const upload = document.getElementById('#upload');
const acceptType = ['image/png','image/jpeg','image/jpg'];
const maxSize = 3 * 1024 *1024;

upload.addEventListener('change',function(e){
	let file = e.target.files[0];
	if(!file)return;
	let {type:fileType,size:fileSize} = file;
	if(!acceptType.includes(fileType)){
		alert(`不支持${fileType}类型文件`);
		upload.value = '';
		return;
	}
	if(fileSize > maxSize){
		alert(`文件大小超出3MB!`);
		upload.value = '';
		return;
	}
	convertImageToBase64(file,function(base64Image){
		compress(base64Image,uploadToServer);
	});
})

上一篇:base64和图片互转


下一篇:NetTopologySuite Geometry&WKT&WKB&GeoJSON互转