layui中實現上傳圖片壓縮

小盆友灬發表於2018-08-28

一、關於js上傳圖片壓縮的方法,百度有很多種方法,這裡我參考修改了一下:

function photoCompress(file, w, objDiv) {
    var ready = new FileReader();
    /*開始讀取指定的Blob物件或File物件中的內容. 當讀取操作完成時,readyState屬性的值會成為DONE,如果設定了onloadend事件處理程式,則呼叫之.同時,result屬性中將包含一個data: URL格式的字串以表示所讀取檔案的內容.*/
    ready.readAsDataURL(file);
    ready.onload = function() {
        var re = this.result;
        canvasDataURL(re, w, objDiv);
    }
};
function canvasDataURL(path, obj, callback) {
    var img = new Image();
    img.src = path;
    img.onload = function() {
        var that = this;
        // 預設按比例壓縮
        var w = that.width,
            h = that.height,
            scale = w / h;
        w = obj.width || w;
        h = obj.height || (w / scale);
        var quality = 0.5; // 預設圖片質量為0.7
        //生成canvas
        var canvas = document.createElement(`canvas`);
        var ctx = canvas.getContext(`2d`);
        // 建立屬性節點
        var anw = document.createAttribute("width");
        anw.nodeValue = w;
        var anh = document.createAttribute("height");
        anh.nodeValue = h;
        canvas.setAttributeNode(anw);
        canvas.setAttributeNode(anh);
        ctx.drawImage(that, 0, 0, w, h);
        // 影像質量
        if(obj.quality && obj.quality <= 1 && obj.quality > 0) {
            quality = obj.quality;
        }
        // quality值越小,所繪製出的影像越模糊
        var base64 = canvas.toDataURL(`image/jpeg`, quality);
        // 回撥函式返回base64的值
        callback(base64);
    }
}
function convertBase64UrlToBlob(urlData) {
    var arr = urlData.split(`,`),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]),
        n = bstr.length,
        u8arr = new Uint8Array(n);
    while(n--) {
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new Blob([u8arr], {
        type: mime
    });
}

以上是壓縮圖片的方法,核心是將圖片放入canvas內,再用canvas.toDataURL方法進行壓縮,最後生成一個Blob物件。

注:因為牽扯到canvas,所以低版本瀏覽器應該是不支援的吧(有待考證)

 

二、圖片壓縮的方法有了,怎麼使用呢?怎麼上傳到後臺呢?往下看!

html部分:

   
        <form action="file/uploadDoc" enctype="multipart/form-data" method="post" id="form">
            <input type="file" id="file" name="file" />
            <input type="submit" value="上傳" />
        </form>

 


js部分:

      $("#file").change(function() {
                var formData = new formData("form");
                var file = this.files[0];
                photoCompress(file, {
                    quality: 0.5,
                }, function(base64Codes) {
                    var bl = convertBase64UrlToBlob(base64Codes);
                    formData.set("file", bl, file.name);
                });
            });

當選擇檔案以後,使用photoCompress方法對上傳的圖片進行壓縮,photoCompress方法的第二個引數還可以傳入長寬等引數,具體可以看photoCompress這個方法,quality是用來設定壓縮後圖片質量的,越小質量越差,表現出來就是圖片越模糊,但是相應的體積就越小。

最後使用formData.set(key,value,name)方法,將現有的name為file的表單元素的值改變。這個方法有三個引數,第一個是key值,也就是表單裡對應的元素的name值(如果不存在會自行新增),第二個值是value值,第三個是選填的值,如果第二個值為blob物件或者file物件,則第三個值表示檔名。

 

當然,如果你不想用form表單提交,你也可以用ajax提交的方法:

html:   

 

        <form enctype="multipart/form-data" method="post" id="form">
            <input type="file" id="file" name="file" />
            <input type="button" value="上傳" id="uploadBtn"/>
        </form>

 

 

 

有些許的變化,form沒有了action,上傳的按鈕type改為了button

js部分給按鈕新增一個點選事件,其他也沒有變化,不做過多贅述:

            $("#uploadBtn").click(function () {
                var formData = new formData("form");
                $.ajax({
                    type:"post",
                    url:"",
                    async:true,
                    data:formData,
                    success:function (data) {
                        
                    },
                    error:function (e) {
                        
                    }
                });
            });

 

 

三、結合layui踩的一些坑,以及最終的解決方法。

先看html部分:

<button type="button" class="layui-btn" id="upImg">上傳圖片</button>
<div id="img_list">
</div>
<input type="button" id = "btnHide" class="none">

 

就是這麼簡單。為什麼要再寫一個隱藏的按鈕,之後解釋。

js部分:

            layui.use(`upload`, function() {
                var upload = layui.upload;
                var uploadInst = upload.render({
                    elem: `#upImg`,
                    url: `/upload/`,
                    auto: false,
                    bindAction: "#btnHide",
                    choose: function(obj) {
                        var files = obj.pushFile();
                        var index, file, indexArr = [];
                        for(index in files) {
                            indexArr.push(index);
                        };
                        var iaLen = indexArr.length;
                        file = files[indexArr[iaLen - 1]];
                        for(var i = 0; i < iaLen - 1; i++) {
                            delete files[indexArr[i]];
                        }
                        try {
                            if(file.size > 200 * 1024) {
                                delete files[index];
                                photoCompress(file, {
                                    quality: 0.5,
                                }, function(base64Codes) {
                                    var bl = convertBase64UrlToBlob(base64Codes);
                                    obj.resetFile(index, bl, file.name);
                                    $("#btnHide").trigger("click");
                                });
                            } else {
                                $("#btnHide").trigger("click");
                            }
                        } catch(e) {
                            $("#btnHide").trigger("click");
                        }
                    },
                    done: function(res) {
                        //這裡把後臺返回的資料進行操作,展示上傳完成的圖片,具體資料格式參考layui的API
                    },
                    error: function() {

                    }
                });
            });

原理:在選擇照片之後,獲取檔案,轉換為blob物件,使用resetFile方法對檔案列隊裡的檔案進行重新設定,然後再觸發上傳事件。

踩的坑:

1、resetFile這個方法是layui 2.3.0 新增的,所以首先要確保layui的版本是最新的。

2、我把auto設定為false,點選btnHide時觸發上傳,我也試過自動上傳,自動上傳的話,這些操作的程式碼就要寫在before方法中(具體看layui的API),然而我發現自動上傳修改檔案列隊的方法總是在上傳成功之後才呼叫,這就導致實際上傳的圖片其實沒有壓縮,至於為什麼是這個執行順序我隱約覺得是不是圖片轉碼,放入canvas的時候耽誤了……具體原因我不明白,所以我用手動上傳,確認修改了檔案列隊,再手動觸發上傳。

3、關於檔案列隊,多次上傳檔案,檔案列隊也就是obj.pushFile()返回的是多個檔案的物件,而且這些檔案物件的key還是一串隨機數……所以我的思路是上傳一次,就用delete方法刪除佇列中已上傳過的檔案。至於為什麼不直接全部清空,因為考慮到不需要壓縮的情況,如果全部清空,不壓縮,就沒有執行resetFile方法,檔案列隊裡就沒有檔案,會報錯。

 

相關文章