H5圖片壓縮與上傳

接到需求,問前端是否能夠壓縮圖片?由於有的圖片太大,傳到服務器上再壓縮太慢了。意識裏沒有這麼玩過,早上老大丟來一個知乎連接,一看,原來前輩們已經用canvas實現了(爲本身的見識羞愧3秒鐘,再立刻開幹)!。html

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 }); };

壓縮完成會獲得相似這樣的圖片:ios

data:image/jpeg 這樣的格式已經應用的不少了,不少樣式裏面的背景圖片直接就是這樣。git

須要說明的是有兩點,這裏的resImg是一個預覽圖片,是已經存在於文檔中的,若是你不須要預覽,而只是建立一個img用來壓縮(document.createElement("img")),這會少一個tagName屬性。你能夠修改源碼或者本身加上這個屬性。源碼中會根據tagName進行判斷,不存在的話會報錯:github

 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);
        }
    };

另外這是一個耗時的操做,若是是多張圖片進行壓縮,不能直接調用,須要稍微變換一下,否則會致使前面的圖片沒有壓縮完成就進入到了後面的圖片。canvas

 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控制器方法以下:api

 [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轉換:mvc

 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/

小結:基本上就是這樣了,前端可以壓縮圖片的話,確實省了流量和時間。 插件是在上一篇的基礎上進行改進的。

相關文章
相關標籤/搜索