zoukankan      html  css  js  c++  java
  • 移动端图片上传的那些事儿

    宿主环境:微信公众号

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

    拿到需求后在心中默默的将整个实现的思路缕了一遍。既然是在微信公众号上面开发,那么调用微信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);
        });
    }

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

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

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

  • 相关阅读:
    shift
    start
    exit
    call
    goto
    Activity生命周期(二)
    color 和 mode
    pause 和 title
    day 4 飞机大战-面向对象
    day 3 创建窗口,移动-函数版
  • 原文地址:https://www.cnblogs.com/bian21/p/14782137.html
Copyright © 2011-2022 走看看