移动端图片上传的那些事儿

宿主环境:微信公众号

应公司业务需要,需在公众号上增加一个人脸采集的入口,核心是图片的上传和预览。

拿到需求后在心中默默的将整个实现的思路缕了一遍。既然是在微信公众号上面开发,那么调用微信sdk提供的chooseImage和previewImage两个方法便能轻松实现这个功能。我只需要将chooseImage的回调中返回的serverId传给后台,让他们根据serverId去微信服务器下载并上传至数据库就行。这一系列图片上传的事情相当于都是“关我屁事”了..

内心一阵窃喜,心想着这个这个功能应该个把小时可以搞定,搞完又可以在线划水了,开心~

a few minutes later~

后台大佬跟我说,通过拿serverId去微信服务器下载图片需要进行一系列的验证,并且本次上传的图片是直接上传至第三方服务器。因此需要我以文件流的形式传给他们。

为了响应后台大佬需要,我调整了下实现方式,将chooseImage回调中返回的localImgData转换成文件(该字段的返回值就是当前图片对应的base64的地址,忍不住要给wx点赞,解放生产力啊。。),并以表单的方式传给后台,大功告成。

核心代码如下:

1 function convertBase64UrlToBlob(urlData) {
2     let bytes = window.atob(urlData.split(',')[1]);
3     let ab = new ArrayBuffer(bytes.length);
4     let ia = new Uint8Array(ab);
5     for (let i = 0; i < bytes.length; i++) {
6         ia[i] = bytes.charCodeAt(i)
7     }
8     return new Blob([ab], {type: 'image/jpeg'});
9 }

功能临近上线,测试大佬的一句:“安卓机不行啊,图片上传时一直在转圈圈..”。简直是晴天霹雳,只能强装镇定,打开调试工具,查起了问题原因。发现安卓机上chooseImag返回的localImgData压根不是base64,而是微信服务器上一串特定标识的字符串,与IOS下的完全不一致,简直超乎我的想象。(我要把我上面给wx的点赞收回..)

迫于无奈,我只能用回最原始的方式了,使用 input 来实现选择图片,图片预览的话还是使用微信sdk提供的previewImage;

1 function changeFile(ev) {
2     let reader = new FileReader();
3     let file = ev.target.files[0];
4 
5     reader.readAsDataURL(file);
6     reader.onload = () => {
7         this.imageUrl = reader.result;
8     }
9 }

 当我正想安安静静的做做其他事情,忘记上面这个功能时,产品大佬跟我说,有客户反馈,选完图片后,呈现出来的图片被旋转了,我... 真所谓是一波三折... 

由于之前没有处理过类似的问题,因此只能硬着头皮研究起了各种解决方案。在github上发现有个javascript库,用于读取图片的元数据,元数据中包括Orientation(旋转角度),正是我想要的,因此基于该工具类重新开始改造起了我的代码。

1、首先写了一个工具方法,用于获取图片被旋转的角度。

function getOrientation(file) {
    return new Promise((resolve, reject) => {
        EXIF.getData(file, function () {
            let Orientation = EXIF.getTag(this, "Orientation");
            resolve(Orientation)
        });
    })
}

2、其次,根据被旋转的角度,使用canvas绘制并生成图片。该方法中一并解决了图片过大上传至服务器时,接口响应很慢的问题。

function compressImg({
    img,
    type = "image/jpeg",
    mx = "750",
    mh = "750",
    orientation = 1
} = opts) {
    return new Promise((resolve, reject) => {
        const {
             originWidth,
            height: originHeight
        } = img;

        // 最大尺寸限制
        const maxWidth = mx;
        // 目标尺寸
        let targetWidth = originWidth;
        let targetHeight = originHeight;
        if (originWidth > maxWidth) {
            // 宽图片
            targetWidth = maxWidth;
            targetHeight = Math.round(maxWidth * (originHeight / originWidth));
        }


        // 创建画布
        const canvas = document.createElement("canvas");
        const context = canvas.getContext("2d");

        // 图片绘制
        // context.clearRect(0, 0, targetWidth, targetHeight);
        if (!window.imgIsRotate) {
            switch (orientation) {
                case 6: // 旋转90度
                    canvas.width = targetHeight;
                    canvas.height = targetWidth;
                    context.rotate(Math.PI / 2);
                    // (0,-targetHeight) 从旋转原理图那里获得的起始点
                    context.drawImage(img, 0, -targetHeight, targetWidth, targetHeight);
                    break;
                case 3: // 旋转180度
                    canvas.width = targetWidth;
                    canvas.height = targetHeight;
                    context.rotate(Math.PI);
                    context.drawImage(img, -targetWidth, -targetHeight, targetWidth, targetHeight);
                    break;
                case 8: // 旋转-90度
                    canvas.width = targetHeight;
                    canvas.height = targetWidth;
                    context.rotate(3 * Math.PI / 2);
                    context.drawImage(img, -targetWidth, 0, targetWidth, targetHeight);
                    break;
                default:
                    canvas.width = targetWidth;
                    canvas.height = targetHeight;
                    context.drawImage(img, 0, 0, targetWidth, targetHeight);
            }
        } else {
            canvas.width = targetWidth;
            canvas.height = targetHeight;
            context.drawImage(img, 0, 0, targetWidth, targetHeight);
        }

        canvas.toBlob(function (blob) {
            resolve(blob);
        }, type || "image/jpeg", 0.92);
    });
}

至此,图片的选择和预览算是真正的告一段落了。

通过本次实践,从发现问题到解决问题的这一过程,对自己来说,收获还是很多的,希望再接再厉吧!

注:本文中如果有描述不当或者有偏差的地方,希望各位大哥见谅并指正,大家一起加油。

原文地址:https://www.cnblogs.com/bian21/p/14782137.html