思路如下:
使用原生input标签实现图片上传时,如果你的写法是如下所示:
<input type="file" id="upload_file" @change="fileChange($event)" style="display: none" data-type="front"
>
fileChange(el) {if (!el.target.files[0].size) return;let currentType = el.target.dataset.typethis.fileList(el.target.files,currentType);el.target.value = ''
},
// 格式限制
fileList(files,currentType) {for (let i = 0; i < files.length; i++) {
if(files[i].type=='image/jpeg'||files[i].type=='image/jpg'||files[i].type=='image/png'||files[i].type=='image/webp'){this.fileAdd(files[i],currentType);} else {return}}
},
// 获取选择的图片的base64地址
fileAdd(file,currentType) {let reader = new FileReader();reader.vue = this;reader.readAsDataURL(file);let _this = this;reader.onload = function () {file.src = this.result; // 图片的base64地址}
}
那你在手机中表现为:可能部分安卓只能从相册中选择照片,ios则可以拍摄、相册二选一。
为了在安卓手机也可以实现(拍摄、相册二选一)的需求,我们尝试给input标签增加accept="image/*"的属性,然后引出了新的问题
问题描述:发现在部分老机型(2016年买的手机)中当用户拍照后并没有触发input的change事件,网上有讨论说是input的value值没有改变,但如你所见,input标签上我们并没有绑定任何的value值。
最终,因为我们要实现的h5页面仅在微信浏览器环境使用,故我们改成在微信浏览器环境调用wx.chooseImage这个api来实现,关键代码如下所示:
fileClick(type) {var that=thisif(that.supportWxApi) { // 微信环境,用微信提供的apiwx.chooseImage({count: 1, // 最多可以选择的图片张数,默认9sizeType: ['original','compressed'], // 可以指定是原图还是压缩图,默认二者都有'original', 'compressed'sourceType: ['album','camera'], // 可以指定来源是相册还是相机,默认二者都有'album', 'camera'success: function (res) {/*** 获取图片数据*/wx.getLocalImgData({localId: res.localIds[0].toString(),success: function (result) {const localData = result.localData;let imageBase64 = '';if (localData.indexOf('data:image') == 0) {//苹果的直接赋值,默认生成'data:image/jpeg;base64,'的头部拼接imageBase64 = localData;} else {//此处是安卓中的坑!在拼接前需要对localData进行换行符的全局替换//此时一个正常的base64图片路径就完美生成赋值到img的src中了imageBase64 = 'data:image/jpeg;base64,' + localData.replace(/\n/g, '');}if(type=='front') {that.compressImg(imageBase64,function(base64Codes){console.log('base64Codes',base64Codes)})} else if(type=='back') {that.compressImg(imageBase64,function(base64Codes){console.log('base64Codes',base64Codes)})}}});}});} else { // 非微信环境,用原生的input上传图片// 用click事件去触发change事件document.getElementById('upload_file').click()}},
图片压缩
服务器那边对图片的大小有要求,故我们利用canvas来实现图片等比例压缩,
压缩图片的关键函数代码如下所示:
// 图片压缩compressImg(path,callback) {var img = new Image();img.src = path;var objCompressed = {}var _this = this;img.onload = function() {//默认压缩后图片规格var quality = 0.7;var w = this.width;var h = this.height;//实际要求if (w > h) {let scale = h/wh = 1300;w = h/scale;} else {let scale = w/hw = 1300;h = w/scale;}// w = objCompressed.width || w;// h = objCompressed.height || (w / scale);//生成canvasvar canvas = document.createElement('canvas');var ctx = canvas.getContext('2d');// 创建属性节点var anw = document.createAttribute("width");anw.nodeValue = w;var anh = document.createAttribute("height");anh.nodeValue = h;canvas.setAttributeNode(anw);canvas.setAttributeNode(anh);ctx.drawImage(this, 0, 0, w, h);var base64 = canvas.toDataURL('image/jpeg', quality);callback(base64);//回调函数返回base64的值}},
图片压缩函数的使用方式如下:
that.compressImg(imageBase64,function(base64Codes){// imageBase64是压缩前的图片console.log('压缩后的大小:',that.showSize(base64Codes))
})
获取base64图片的大小,返回kb数字
showSize(base64url) {//把头部去掉let str = base64url.replace('data:image/jpeg;base64,', '');// 找到等号,把等号也去掉let equalIndex = str.indexOf('=');if (str.indexOf('=') > 0) {str = str.substring(0, equalIndex);}// 原来的字符流大小,单位为字节let strLength = str.length;// 计算后得到的文件流大小,单位为字节let fileLength = parseInt(strLength - (strLength / 8) * 2);// 由字节转换为kblet size = "";size = (fileLength / 1024).toFixed(2);let sizeStr = size + "";let index = sizeStr.indexOf(".");let dou = sizeStr.substr(index + 1, 2);if (dou == "00") {return sizeStr.substring(0, index) + sizeStr.substr(index + 3, 2)}return size;},