分享

在浏览器端用H5实现图片压缩上传

 Earlycl6i7o9ek 2020-02-21

多年互联网行业经验,对HTML5,node端,前端框架,构建工具有浓厚的兴趣,目前担任专车前端组的技术负责人

一、需求的场景:

在我们的需求中需要有一个在手机浏览器端,用户实现上传证件照片的功能,我们第一版上了一个最简版,直接让用户在本地选择图片,然后上传到公司公共的服务器上。 功能实现后我们发现一个问题,公司公共的服务器有 2M 图片的限制,而用户手机目前绝大多数都支持高清拍照,尺寸普遍在 3000 x 2000 的大小;

所以我们采用了在浏览器端通过 HTML5 的 fileReader 接口来处理上传文件的大小,将重新处理压缩后的文件在传给后端,这样在保证了图片基本质量(由于证件图片,我们只关心证件号码是否清晰)的情况下,也能够做到网络传输内容的最小化,让上传变得更快,给用户较好的体验。

二、逐步分析:

首先我们上传的文件在 input 事件的默认参数里面是一个 file 类型,他是 Binary Large Object 的一个子集,需要将 Blob 类型转换成 DataUrl,能够被 HTML5 本地的 Canvas 画布解析,通过 Canvas 画布根据一定的压缩比例重新压缩后,再将图片转换成 Blob 传到公司公共的图片服务器上。

  1. select local file -> Blob -> DataUrl -> Canvas compress -> DataUrl -> Blob -> Upload file

三、具体实现逻辑:

1. 监听本地 input 框 change 事件,当内容变化,从回调函数的参数中拿到 file 文件; 2. 判断当前浏览器是否支持本地压缩(是否支持 HTML5 的 fileReader 方法); 3. 如果不支持本地压缩,采用传统方式直接将原始图片上传到服务器上; 4. 如果支持浏览器的本地压缩,那么进入到本地压缩的流程; 5. 压缩完成后将最新的 blob 类型文件传递到公司服务器上;

  1.       // 用户在浏览器本地选择上传图片

  2.       /*

  3.        * option = {

  4.        *       el: element, // input file element

  5.        *       width: 800,

  6.        *       height: 600,

  7.        *       rate: 1,

  8.        *       cb: callback

  9.        * }

  10.        *

  11.        */

  12.        function compressUploadImageAsClient(option) {

  13.            var opt = {

  14.                el: option.el,

  15.                width: option.width || 800,

  16.                height: option.height || 600,

  17.                rate: option.rate || 1,

  18.                cb: option.cb || function() {},

  19.                postUrl: option.postUrl || '',

  20.                postLoad: option.postLoad || function() {},

  21.                postError: option.postError || function() {},

  22.                postAbort: option.postAbort || function() {}

  23.            };

  24.            opt.el.addEventListener('change', function(e) {

  25.                // 如果不支持H5的filereader方法,直接用原图上传

  26.                if(typeof FileReader != 'function') {

  27.                    // sendFile(e.target.files);

  28.                    postFileToServer([e.target.files[0]], opt.postUrl, opt.postLoad, opt.postError, opt.postAbort);

  29.                    return;

  30.                }

  31.                // 将blob类型转换成DataUrl

  32.                readBlobAsDataURL(e.target.files[0], function(url, size) {

  33.                      // 拿到url类型的图片之后,通过canvas进行压缩转换

  34.                    readBase64AsBlob({

  35.                        url: url,

  36.                        size: size,

  37.                        width: 800,

  38.                        height: 600,

  39.                        rate: 1,

  40.                        callback: function(dataUrl) {

  41.                            // 在压缩完成的回调函数中得到最新的图片DataUrl,将其转换成服务端接口能够识别的Blob类型

  42.                            var blob = dataURLtoBlob(dataUrl);

  43.                            // 调用上传服务器图片的接口

  44.                            postFileToServer([blob], opt.postUrl, opt.postLoad, opt.postError, opt.postAbort);

  45.                        }

  46.                    });

  47.                });

  48.            }, false)

  49.        }

  1.        // file对象转换成dataurl

  2.        function readBlobAsDataURL(blob, callback) {

  3.            var a = new FileReader();

  4.            a.onload = function(e) {

  5.                callback(e.target.result, blob.size);

  6.            };

  7.            a.readAsDataURL(blob);

  8.        }

  1.        // dataurl转换成blob

  2.        function dataURLtoBlob(dataurl) {

  3.        var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],

  4.            bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);

  5.            while(n--){

  6.                u8arr[n] = bstr.charCodeAt(n);

  7.            }

  8.            return new Blob([u8arr], {type:mime});

  9.        }

  1.        // blob转换成dataUrl,并且通过canvas画布压缩重新生成新的dataUrl

  2.        /*

  3.        * option: {

  4.        *     url: image url,

  5.        *     size: image size,

  6.        *     width: 800,

  7.        *     height: 600,

  8.        *     rate: 1,

  9.        *     callback: callback

  10.        * }

  11.        */

  12.        function readBase64AsBlob(option) {

  13.            var opt = {

  14.                url: option.url,

  15.                size: option.size,

  16.                width: option.width || 800,

  17.                height: option.height || 600,

  18.                rate: option.rate || 0.6,

  19.                callback: option.callback || function(url) {}

  20.            };

  21.            var img = new Image();

  22.            img.src = opt.url;

  23.            img.onload = function(){

  24.                var canvas = document.createElement('canvas'), //创建canvas元素

  25.                    width = img.width, //确保canvas的尺寸和图片一样

  26.                    height = img.height;

  27.                console.log('压缩前图片的尺寸大小:', width, height);

  28.                // 根据最大尺寸 800x600的大小,按比例重新设置图片的尺寸

  29.                var neww = opt.width;

  30.                var newh = opt.height;

  31.                // 当图片的宽高分别大于800,600的情况下,在对其进行尺寸的压缩(尺寸压缩对最终size的减小有很大作用)

  32.                if(width > opt.width && height > opt.height) {

  33.                    if(height/width > opt.height/opt.width) {

  34.                        newh = opt.height;

  35.                        neww = (opt.height/height) * width;

  36.                    } else {

  37.                        newh = (opt.width/width) * height;

  38.                        neww = opt.width;

  39.                    }

  40.                }

  41.                // 压缩率

  42.                var rate = opt.rate;

  43.                if(opt.size < 1024*1024*2) {

  44.                    // 小于2m处理;

  45.                    rate = opt.rate * 0.6;

  46.                } else if(opt.size < 1024*1024*4) {

  47.                    // 2m到4m之间

  48.                    rate = opt.rate * 0.4;

  49.                } else if(opt.size < 1024*1024*8) {

  50.                    // 4m到8m之间

  51.                    rate = opt.rate * 0.3;

  52.                } else {

  53.                    // 大于8m的图片

  54.                    rate = opt.rate * 0.2;

  55.                }

  56.                canvas.width = neww;

  57.                canvas.height = newh;

  58.                canvas.getContext('2d').drawImage(img, 0, 0, neww, newh); //将图片绘制到canvas中

  59.                var dataURL = canvas.toDataURL('image/jpeg', rate); //转换图片为dataURL

  60.                opt.callback(dataURL);

  61.            }

  62.        }

  1.        // 通过ajax上传图片到公司服务器上

  2.        function postFileToServer(files, posturl, success, fail, abort) {

  3.            if (!files || files.length < 1) {

  4.                alert('上传的文件不能为空');

  5.                return;

  6.            }

  7.            alert('压缩后的图片对象:' files[0].size);

  8.            var formData = new FormData();     // 创建一个表单对象FormData

  9.            // formData.append('submit', 'Submit');  // 往表单对象添加文本字段

  10.             var fileNames = '';

  11.            for (var i = 0; i < files.length; i ) {

  12.                var file = files[i];    // file 对象有 name, size 属性

  13.                formData.append( 'file[' i ']', file);       // 往FormData对象添加File对象

  14.                fileNames = file.name ' ';

  15.            }

  16.            var xhr = new XMLHttpRequest();

  17.            xhr.addEventListener('load', function(e) {

  18.                success(e);

  19.            }, false);

  20.            xhr.addEventListener('error', function(e) {

  21.                error(e);

  22.            }, false);

  23.            xhr.addEventListener('abort', function(e) {

  24.                abort(e);

  25.            });

  26.            xhr.open('post', posturl, true);

  27.            xhr.send(formData);

  28.        }

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多