canvas应用的图片压缩算法实现 前端图片压缩应用场景前端页面限制用户只可以上传5MB大小的图片 前端在接受到用户上传的图片之后,服务器只允许上传1MB大小的图片,此时需要前端将图片先进行压缩,压缩之后再调用图片上传接口将图片上传第一步:创建input元素:
前端图片压缩应用场景
- 前端页面限制用户只可以上传5MB大小的图片
- 前端在接受到用户上传的图片之后,服务器只允许上传1MB大小的图片,此时需要前端将图片先进行压缩,压缩之后再调用图片上传接口将图片上传
第一步:创建input元素,监听input表单域的change事件
- 创建一个input元素,并且设置HTML5新增的type为file
- 监听input元素的change事件,并通过e.target.files拿到用户上传的文件列表数组
- 获取用户上传的文件对象,校验其是否为空
- 校验文件的type类型是否为图片类型
- 校验文件的size大小是否超出5MB
- 如果校验通过,那么才开始进行图片压缩
<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格式字符串
- 创建一个浏览器内置的FileReader对象的实例reader
- 调用reader.readAsDataURL()方法,参数接收一个blob对象或者file对象
- 调用该方法之后会开始读取传入的文件file,读取完成后会触发load完成事件
- 在load事件中,通过e.target.result或者reader.result获取file对应的base64格式字符串
- 执行传入的callback函数,此时开始执行compress压缩函数
- 将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方法,开始压缩图片
- 创建一个image对象,为了方便获取用户上传图片的原始宽高
- 设置最大宽度和高度
- 将image的src属性设置为base64Image,并监听image的load事件
- 当load事件触发后,在事件回调函数中进行如下逻辑:
- 设置压缩比ratio
- 设置是否需要压缩的变量needCompress
- 校验原始宽度超出宽度,计算压缩比,如果超出,此时最大高度需要等比例变化
- 校验原始高度超出高度,计算压缩比,如果超出,此时最大宽度需要等比例变化
- 如果原始宽高都没有超出,那么需要将最大宽高的值修改为原始宽高
- 动态创建一个canvas元素
- 设置canvas元素的宽高属性分别为最大宽度和高度后插入到页面中
- 设置canvas的visibility属性为hidden或者visibile,看具体项目需求
- 获取ctx 2d上下文对象
- 清空上一次绘图的画布ctx.clearRect(0,0,最大宽度,最大高度)
- 绘制图片ctx.drawImage(image,0,0,最大宽度,最大高度)绘制出图片,此时已经将图片的尺寸进行了压缩
- 输出图片canvas.toDataURL('image/jpeg',0.8);前者是输出格式,后者是输出质量。选jpeg本来就会对图片进行压缩,输出质量也会进行压缩。
- 拿到上一步输出的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;}
第四步:将图片上传至服务器
- 接收传递的压缩后图片的base64字符串compressImage
- 将其上传至服务器
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); });})