移动端图片压缩上传实现

您所在的位置:网站首页 安卓canvas无法透明为黑色 移动端图片压缩上传实现

移动端图片压缩上传实现

2023-03-28 10:24| 来源: 网络整理| 查看: 265

移动端图片压缩上传实现

移动端上传的图片一般都是手机照片,现在的手机都是高清像素,一张图片都在三四兆,直接上传不仅传输速度慢,而且如果用户使用的是流量,势必会耗费大量流量。

H5的各种API在移动端的主流浏览器都得到了很好的支持,比如案例中用到的FileReader、Blob、FormData、canvas等API,所以压缩上传图片在前端已经是必备的操作。

压缩上传基本操作流程: 图片上传后使用FileReader将文件读取成base64 创建Image,设置src属性为图片base64 创建canvas,绘制Image 调用canvas的toDataURL方法压缩,返回压缩后的base64 将base64转成Blob对象 创建FormData对象,append Blob对象,提交给服务端

下面是每一步的具体实现以及一些坑(比如:API的兼容性、IOS图片旋转、底色等),并贴上全部代码。

调用系统录制功能 调用系统相机 import EXIF from 'exif'; 一、监听input的change事件,读取成base64。如果照片是竖着拍的,在IOS手机上传后图片会被旋转。这里需要用到一个库EXIF),可以获取相片的属性,比如曝光度、拍照方向、GPS等。图片加载完成后,在压缩前需要解决IOS图片是否被旋转的问题和图片压缩格式的问题。

图片旋转

问题: IOS竖着拍的照片会旋转。 解决: 首先创建临时canvas,绘制图片,旋转成正确方向

canvas的toDataURL() 参数type的默认值是 “image/png”,如果传入的类型非“image/png”,但是返回的值以“data:image/png”开头,那么该传入的类型是不支持的。把类型统一设成jpeg,也就是统一用canvas.toDataURL('image/jpeg', 0.3) ,压缩默认值 0.92,这里我设的0.3。

let inp = document.getElementById('upload'); inp.onchange = function (event) { let file = event.target.files[0]; let reader = new FileReader(); let Orientation; // 读取文件转base64 reader.readAsDataURL(file); // 读取完成 reader.onload = function () { let result = this.result; /** * result.length 的单位是字节 * 如果图片小于100K直接上传,反之压缩图片 */ if (result.length 1) { // 倍数开方 (相当于面积为多少倍,则宽高对应的倍数需对面积倍数开方) ratio = Math.sqrt(ratio); // 宽高对应的值 width /= ratio; height /= ratio; } else { ratio = 1; } // 画布宽高 canvas.width = width; canvas.height = height; // 铺底色 ctx.fillStyle = '#fff'; // 绘制矩形 ctx.fillRect(0, 0, width, height); // 如果缩放比例后画布像素仍大于100万像素 则使用瓦片绘制, 反之直接绘制 let count = width * height / 1000000; if (count > 1) { // 创建瓦片 获取2d上下文 let tcanvas = document.createElement('canvas'); let tctx = tcanvas.getContext('2d'); /** * 瓦片数量 = count的平方 + 1 * +1不是必须得,是为了瓦片更小,数量更多一些 */ count = ~~(Math.sqrt(count) + 1); // 比如count为2.3 则转成3 let tWidth = ~~(width / count); let tHeight = ~~(height / count); // 瓦片的宽高 tcanvas.width = tWidth; tcanvas.height = tHeight; for (let i = 0; i < count; i++) { for (let j = 0; j < count; j++) { tctx.drawImage(image, i * tWidth * ratio, j * tHeight * ratio, tWidth * ratio, tHeight * ratio, 0, 0, tWidth, tHeight); console.log(tcanvas.width, tcanvas.height, tcanvas.width * tcanvas.height); ctx.drawImage(tcanvas, i * tWidth, j * tHeight, tWidth, tHeight); } } } else { // 直接绘制 ctx.drawImage(image, 0, 0, width, height); } return canvas; } 完成图片压缩后,先将base64提取出来,再实例化一个ArrayBuffer,然后将字符串以8位整型的格式传入ArrayBuffer,再通过Blob对象(可能需要兼容Blob),将8位整型的ArrayBuffer转成二进制对象blob,然后把blob对象append到formdata里,再提交给后台。 function convertBase64UrlToBlob(urlData) { let bytes = window.atob(urlData.split(',')[1]); // 处理异常,将ascii码小于0的转换为大于0 let ab = new ArrayBuffer(bytes.length); let ia = new Uint8Array(ab); for (let i = 0; i < bytes.length; i++) { ia[i] = bytes.charCodeAt(i); } // 二进制对象 return getBlob([ab], "image/jpeg"); } 兼容Blob对象 /** * Blob对象的兼容性写法 * @param buffer 数据流 * @param format 表示将会被放入到blob中的数组内容的MIME类型。类型默认 '' */ function getBlob(buffer, format = 'image/jpeg') { try { return new Blob(buffer, { type: format }); } catch (e) { let blob = new (window.BlobBuilder || window.WebKitBlobBuilder || window.MSBlobBuilder)(); buffer.forEach(function (buf) { blob.append(buf); }); return blob.getBlob(format); } } 低版本的Android机不支持FormData,需要做兼容处理。首先判断是否需要兼容 function needsFormDataShim() { return ~navigator.userAgent.indexOf('Android') && ~navigator.vendor.indexOf('Google') && !~navigator.userAgent.indexOf('Chrome') && navigator.userAgent.match(/AppleWebKit\/(\d+)/).pop() { console.log(response); }) .catch(error => { console.log(error); }); // axios 会根据提交的文件类型,设置相应的Content-Type类型 } } 旋转图片 /** * @param 旋转的图片 * @param 方向 * @param 绘制的canvas */ function rotateImg(img, direction, canvas) { //最小与最大旋转方向,图片旋转4次后回到原方向 const min_step = 0; const max_step = 3; if (img == null) return; // 缩小比例后的canvas let lessCnavas = compress(img); let {width, height} = lessCnavas; let step = 2; if (step == null) { step = min_step; } if (direction == 'right') { step++; //旋转到原位置,即超过最大值 step > max_step && (step = min_step); } else { step--; step < min_step && (step = max_step); } //旋转角度以弧度值为参数 let degree = (step * 90 * Math.PI) / 180; let ctx = canvas.getContext('2d'); switch (step) { case 0: canvas.width = width; canvas.height = height; ctx.drawImage(lessCnavas, 0, 0); break; case 1: canvas.width = height; canvas.height = width; ctx.rotate(degree); ctx.drawImage(lessCnavas, 0, -height); break; case 2: canvas.width = width; canvas.height = height; ctx.rotate(degree); ctx.drawImage(lessCnavas, -width, -height); break; case 3: canvas.width = height; canvas.height = width; ctx.rotate(degree); ctx.drawImage(lessCnavas, -width, 0); break; } }

以上就是压缩上传的全部实现。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3