vue实现图片压缩的功能进行封装和调用
、单页面实现图片压缩 - 小那 - 博客园 (cnblogs.com)
对上述链接的单页面实现图片压缩的功能进行进一步的修改,并应用到项目中
整体思路:
- 第一次压缩,将图片转为base64
- 第二次压缩,利用canvas画布生成图片
- 最后将base64转为文件流形式返回
function convertImageToBase64(fileinput, callback) { // 创建一个FileReader对象,它允许Web应用程序异步读取存储在计算机上的文件 // 也就是file对象 let reader = new FileReader() let file = fileinput.files[0]; // 添加一个load事件,load会在加载完毕之后进行触发(也就是readAsDataURL读取完毕后触发) reader.addEventListener("load", function (e) { const base64Image = e.target.result // 相当于reader.result 获取文件的Base64 // 回收内存 // callback && callback(base64Image) // 调用callback压缩 compress(base64Image, file, callback) reader = null }) // readAsDataURL方法读取指定的file或blob对象 reader.readAsDataURL(file) } // 压缩算法函数 /* 1.首先拿到了base64的图片字符串 2.创建一个image对象,获得原始图片的宽度和高度 3.对原始图片的宽度和高度进行压缩达到符合条件(第一次压缩-从尺寸压缩) 4.调用canvasAPI进行绘制新的图片 5.绘制成功之后调用canvasAPI进行绘制(canvasAPI支持压缩-二次压缩-从质量压缩) 6.得到压缩后的base64 */ function compress(base64Image, file, callback) { let maxW = 1024; let maxH = 1024; let size = 1024 * 1024 * 2 const image = new Image() // 创建image对象 相当于创建a标签 image.addEventListener('load', function (e) { // image加载完成后就会触发 也就是src加载后 let radio; // 压缩比例 let needCompress = false; // 是否需要压缩 // image.naturalWidth/naturalHeight H5新属性 获取源生图片的宽高 if (file.size > size) { needCompress = true; // 获得压缩宽高过后的大小(保证等比例缩放) radio = image.naturalWidth / maxW maxH = image.naturalHeight / radio // 第一次压缩完成 // 接下来使用canvas进行质量压缩 const canvas = document.createElement('canvas') canvas.height = maxH; canvas.width = maxW; canvas.setAttribute("id", "_compress_") // visibility hidden 需要创建的canvas隐藏 而不是不渲染DOM canvas.style.visibility = 'hidden' document.body.appendChild(canvas) const ctx = canvas.getContext('2d') // canvas.clearRect() 方法清空给定矩形内的指定像素。(x1,y1,width,height) // 防止重新上传覆盖 ctx.clearRect(0, 0, maxW, maxH) // canvas.drawImage() 方法在画布(canvas)上绘制图像、画布或视频。 // 传入 视频/图片对象 起始点x 起始点y 绘制宽 绘制高 ctx.drawImage(image, 0, 0, maxW, maxH) // 接来下就是压缩canvas 通过API将canvas输出成base64格式 /** * @HTMLCanvasElement.toDataURL(type, encoderOptions); 注意调用这是 Canvas的Dom对象而非ctx * @param {String} 可以使用 type 参数其类型, type 图片格式,默认为image/png,图片的分辨率为96dpi。 * @param {Number} encoderOptions 可选 指定图片格式是image/jpeg或image/webp的情况下,可以从0到1区间内进行选择图片的质量(1原质量)。如果超出取值方位,使用默认0.92 * @returns {dataURL} 方法返回一个包含图片展示的 data URI (Base64) */ /* 注意这里是Canvas DOM 节点 而非canvas对象*/ const compressImage = canvas.toDataURL('image/jpeg', 0.8) // 通常压缩是0.8-0.9 let filedata = convertBase64ToBlob(compressImage) callback && callback(filedata); // 压缩完成进行后台传输逻辑 // 压缩完的图片就已经保存在内存(compressImage)中了 // 接下来移除canvas元素 调用DOM.remove() canvas.remove() // 需要的上传预览的话可以单独建一个new image进行预览 const _image = new Image() _image.src = compressImage; document.body.appendChild(_image) //计算压缩比 使用src(base)的长度就可以对比了 // console.log(`压缩比${image.src.length / _image.src.length}`) } else { let filedata2 = new File([file], Date.now() + '.png', { type: file.type }) callback && callback(filedata2); // 无需压缩的文件直接返回 } // 不需要压缩 if (!needCompress) { maxW = image.naturalWidth; maxH = image.naturalHeight; } }) image.src = base64Image; // document.body.appendChild(image) // 挂载 } // 将压缩后图片的base64转成文件流形式返回 function convertBase64ToBlob(dataURI) { var byteString = atob(dataURI.split(',')[1]); var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; var ab = new ArrayBuffer(byteString.length); var ia = new Uint8Array(ab); for (var i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); } // return new Blob([ab], { type: mimeString }); let blob = new Blob([ab], { type: mimeString }); let file = new File([blob], Date.now() + '.png', { type: mimeString }) return file } export default { convertImageToBase64 }
修改过程:
- 新建compressImage.js文件,将方法粘贴到该文件中
- 压缩方法修改
- 原方法的第一步压缩是通过图片的宽高大小,判断是否进行压缩操作,我修改为通过判断文件大小来判断是否进行压缩图片
- 原方法的第二步压缩是无论图片大小都进行canvas压缩,改为通过判断文件大小并进行第一步压缩后再进行canvas压缩
- 增加base64转成文件流的方法,以便传参给后台
调用方法
// 上传图片压缩 import CompressImage from "../utils/compressImage"; CompressImage.convertImageToBase64(fileInput, (fileData) => { fileData.tenantId = SYUserAuth.getTenantId(); FileUpload(fileData).then((res) => { let imgUrl = window.CDN_URL + res.data.data.fileUrl; let length = self.quill.getSelection(true).index; self.quill.insertEmbed(length, "image", imgUrl); }).catch(() => { Message({ message: "上传失败", type: "error" }); }); });