• HTML5实现图片压缩和上传的功能
  • Alice 发表于 2017/3/3 10:45:00 | 分类标签: html5上传图片 html5压缩图片
  •  接到需求,问前端是否可以压缩图片?因为有的图片太大,传到服务器上再压缩太慢了。意识里没有这么玩过,赶紧上网百度一看,原来前辈们已经用canvas实现了(为自己的见识羞愧3秒钟,再马上开干)!。

    canvas压缩

    使用了github上的一个现成库:https://github.com/stomita/ios-imagefile-megapixel,不得不膜拜下stomita这位大神。大体的思路是将图片抽样显示在canvas上,然后用通过canvas.toDataURL方法得到base64字符串来实现压缩。比如在input元素触发change事件之后,读取里面的文件进行操作:

    var fileInput = document.getElementById('fileInput');
      fileInput.onchange = function() {
        var file = fileInput.files[0];
        // 创建一个压缩对象,该构造函数接收file或者blob。
        var mpImg = new MegaPixImage(file);
    // render方法的maxWith,maxHeight,以及quality都决定了压缩图片的质量 var resImg = document.getElementById('resultImage'); mpImg.render(resImg, { maxWidth: 300, maxHeight: 300, quality: 0.5 }); };

    压缩完成会得到类似这样的图片:

    data:image/jpeg 这样的格式已经应用的很多了,很多样式里面的背景图片直接就是这样。

    需要说明的是有两点,这里的resImg是一个预览图片,是已经存在于文档中的,如果你不需要预览,而只是创建一个img用来压缩(document.createElement("img")),这会少一个tagName属性。你可以修改源码或者自己加上这个属性。源码中会根据tagName进行判断,不存在的话会报错:

     MegaPixImage.prototype.render = function (target, options, callback) {
            //....
            target.tagName = target.tagName || "IMG"; //加上这一句
            var tagName = target.tagName.toLowerCase();
            if (tagName === 'img') {
                target.src = renderImageToDataURL(this.srcImage, opt, doSquash);
            } else if (tagName === 'canvas') {
                renderImageToCanvas(this.srcImage, target, opt, doSquash);
            }
            if (typeof this.onrender === 'function') {
                this.onrender(target);
            }
            if (callback) {
                callback();
            }
            if (this.blob) {
                this.blob = null;
                URL.revokeObjectURL(this.srcImage.src);
            }
        };

    另外这是一个耗时的操作,如果是多张图片进行压缩,不能直接调用,需要稍微变换一下,不然会导致前面的图片没有压缩完成就进入到了后面的图片。

     fileSelected: function () {
                        var files = $("#fileImage")[0].files;
                        var count = files.length;
                        console.log("共有" + count + "个文件");
                        for (var i = 0; i < count; i++) {var item = files[i];
                            console.log("原图片大小", item.size);
                            if (item.size > 1024 * 1024 * 2) {
                                console.log("图片大于2M,开始进行压缩...");
    
                                (function(img) {
                                    var mpImg = new MegaPixImage(img);
                                    var resImg = document.createElement("img");
                                    resImg.file = img;
                                    mpImg.render(resImg, { maxWidth: 500, maxHeight: 500, quality: 1 }, function() {
                                      //do some thing
                                    });
                                })(item);
    
                            } 
                            core.previewImage(item);
                        }
                    },

    上传处理

     1.直接post base64字符串

      uploadBase64str: function (base64Str) {
                        var formdata = new FormData();
                        formdata.append("base64str", base64Str);
                        var xhr = new XMLHttpRequest();
                        xhr.upload.addEventListener("progress", function (e) {
                            var percentComplete = Math.round(e.loaded * 100 / e.total);
                            para.onProgress(percentComplete.toString() + '%');
                        });
                        xhr.addEventListener("load", function (e) {
                            para.uploadComplete(xhr.responseText);
                        });
                        xhr.addEventListener("error", function (e) {
                            para.uploadError(e);
                        });
    
                        xhr.open("post", para.base64strUrl, true);
                        xhr.send(formdata);
                    },

    比如这里base64strUrl是/home/MUploadImgBase64Str,MVC控制器方法如下:

     [HttpPost]
            public ActionResult MUploadImgBase64Str(string base64str)
            {
                try
                {
                    var imgData = base64str.Split(',')[1];
                    //过滤特殊字符即可   
                    string dummyData = imgData.Trim().Replace("%", "").Replace(",", "").Replace(" ", "+");
                    if (dummyData.Length % 4 > 0)
                    {
                        dummyData = dummyData.PadRight(dummyData.Length + 4 - dummyData.Length % 4, '=');
                    }
                    byte[] byteArray = Convert.FromBase64String(dummyData);
                    using (System.IO.MemoryStream ms = new System.IO.MemoryStream(byteArray))
                    {
                        var img = System.Drawing.Image.FromStream(ms);
    
                        var path = "~/Content/UploadFiles/mobile/";
                        var uploadpath = Server.MapPath(path);
                        if (!Directory.Exists(uploadpath))
                        {
                            Directory.CreateDirectory(uploadpath);
                        }
                        var saveName = uploadpath + “stoneniqiu” + ".jpg";
                        img.Save(saveName);
                        return Json(saveName);
                    }
                }
                catch (Exception e)
                {
                    return Json(e.Message);
    
                }
            }

    几M的图片能压缩到几十k或者几百k,当然,如果宽度、高度和质量设置的太小,图片就会很失真了。这个字符串怎么获取呢?有两个方法,一个是直接读取src:

     var base641 = resImg.src;

    一个是用canvas转换:

     function getBase64Image(img) {
                    var canvas = document.createElement("canvas");
                    canvas.width = img.width;
                    canvas.height = img.height;
    
                    var ctx = canvas.getContext("2d");
                    ctx.drawImage(img, 0, 0, img.width, img.height);
    
                    var dataURL = canvas.toDataURL("image/jpeg");
                    return dataURL;
    
                    // return dataURL.replace("data:image/png;base64,", "");
                }
     var base64 = getBase64Image(resImg);

    同一张图片,这两者获取到的字符串大小不一样,但图片质量我是分辨不出什么差别。

    比如一个2M的图片,通过getBase64Image方法读到的字符串大小才64k,而src直接读取到的却是270k,各自生成的图片更小。一下分别是原图(2.2M),base64(48k),src(202k)对应的图片。

       

    getBase64Image通过canvas的toDataURL 获取到更小的base64字符串。

    2.也可以在前端转换blob对象,再post到后端

     function dataURItoBlob(dataUrl) {
                  
                    var byteString = atob(dataUrl.split(',')[1]);
    var ab = new ArrayBuffer(byteString.length); var ia = new Uint8Array(ab); for (var i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); } return new Blob([ab], { type: 'image/jpeg' }); }

    3.不压缩的就直接装到formdata中,send到后台。

       uploadFile: function (file) {
                        console.log("开始上传");
                        var formdata = new FormData();
    
                        formdata.append(para.filebase, file);//这个名字要和mvc后台配合
    
                        var xhr = new XMLHttpRequest();
                        xhr.upload.addEventListener("progress", function (e) {
    
                            var percentComplete = Math.round(e.loaded * 100 / e.total);
                            para.onProgress(percentComplete.toString() + '%');
                        });
                        xhr.addEventListener("load", function (e) {
                            para.uploadComplete(xhr.responseText);
                        });
                        xhr.addEventListener("error", function (e) {
                            para.uploadError(e);
                        });
    
                        xhr.open("post", para.url, true);
        
                        xhr.send(formdata);
                    },

    全部的代码:(包含压缩和上传以及demo):

    github:https://github.com/stoneniqiu/h5upload/

    小结:基本上就是这样了,前端能够压缩图片的话,确实省了流量和时间。

  • 请您注意

    ·自觉遵守:爱国、守法、自律、真实、文明的原则

    ·尊重网上道德,遵守《全国人大常委会关于维护互联网安全的决定》及中华人民共和国其他各项有关法律法规

    ·严禁发表危害国家安全,破坏民族团结、国家宗教政策和社会稳定,含侮辱、诽谤、教唆、淫秽等内容的作品

    ·承担一切因您的行为而直接或间接导致的民事或刑事法律责任

    ·您在编程中国社区新闻评论发表的作品,本网站有权在网站内保留、转载、引用或者删除

    ·参与本评论即表明您已经阅读并接受上述条款

  • 感谢本文作者
  • 作者头像
  • 昵称:Alice
  • 加入时间:2013/6/13 0:00:00
  • TA的签名
  • 这家伙很懒,虾米都没写
  • +进入TA的空间
  • 以下内容也很赞哦
分享按钮