jQuery – JavaScript – 教你如何顯示上傳前的多筆預覽圖

* 如果您想要用套件可以來這篇 (2017/03/07更新)


前言可以快速帶過

上一篇 提到預覽圖的製作,使用了 Web APIs 的 FileReader 。後來經過網友的建議與提醒,以及我在實測後發現,一旦使用 FileReader 來讀取檔案,那麼系統資源上會比較吃重,因為瀏覽器會完整讀取你選擇的的每個檔案。所以如果一次選取數十個,瀏覽器就會停擺。網友建議如果只是製作預覽圖,可以改用 URL.createObjectURL() 這個方法。

目標

  1. 多檔預覽。如 Flickr 上傳圖片
  2. 結構化程式碼

解決方法

  1. 使用 vmodel.js 結構化
  2. 使用 URL.createObjectURL() 這個方法來完成

線上測試

 

HTML

<form class="form1">
    <input type='file' class="upl" name="upl[]" multiple>
    <div class="preview">
    </div>
</form>

CSS

.img {
    max-width: 150px; 
    max-height: 150px;
    margin: 5px;
}

jQuery/JavaScript

// 建立一個模組叫做 preview,根節點為 .form1
    $(".form1").vmodel("--preview", true, function (){

        var vs = this;

        // 自動讀取的方法
        this.autoload = ['change_file'];

        // 連續的圖片編碼
        this.imgcode = '';

        // 選取發生改變
        this.change_file = function (){
            vs.root.on("change", ".upl", function (){
                local_show(this);
            });
        }

        // 批次圖片,先清空後再插入
        var local_show = function (input){
            if (input.files && input.files[0]) {
                local_clean();
                local_each_img(input.files);
            }
        }

        // 批次讀取,最後再一次寫入
        var local_each_img = function (files){

            $.each(files, function (index, file){
                console.log(file); //檔案資訊可以在這裡看到
                var src = URL.createObjectURL(file);
                local_create_imgcode(src);
            });

            // 放置預覽元素後重設
            vs.root.find(".preview").html(vs.imgcode);
            local_reset();
        }

        // 建立圖片
        var local_create_imgcode = function(src){
            vs.imgcode += '<img class="img" src="' + src + '">';
        }

        // 清空預覽區域
        var local_clean = function (){
            vs.root.find(".preview").empty();
        }

        // 還原 input[type=file]
        var local_reset = function (){
            vs.imgcode = '';
            vs.root.find(".upl").val(null);
        }

    });

解說

一開始有一個參數,是用來後面做為不間斷的 <img><img><img> 存放。

this.imgcode = '';

接著我們透過 autoload() 可以告訴 vmodel 自動觸發要使用的方法,有 change_file() 這麼一個。

this.autoload = ['change_file'];

首先我們綁定使用者選取圖片的事件,點擊以後會呼叫 local_show(); 並把 input 傳入

// 選取發生改變
this.change_file = function (){
    vs.root.on("change", ".upl", function (){
        local_show(this);
    });
}

當使用者有選取檔案後,才會觸發清空 local_clean() 、批次顯示 local_each_img()

// 批次圖片,先清空後再插入
var local_show = function (input){
    if (input.files && input.files[0]) {
        local_clean();
        local_each_img(input.files);
    }
}

清空,主要是把預覽的區域清空,避免不斷選取會不斷堆疊

// 清空預覽區域
var local_clean = function (){
    vs.root.find(".preview").empty();
}

因為這次的範例,使用者可以複選圖檔,所以我們使用 $.each 把每張圖片呼叫出來,並利用 URL.createObjectURL() 取得獨特的網址,讓該網址做為預覽圖的路徑。

// 批次讀取,最後再一次寫入
var local_each_img = function (files){

    $.each(files, function (index, file){
        console.log(file); //檔案資訊可以在這裡看到
        var src = URL.createObjectURL(file);
        local_create_imgcode(src);
    });

    // 放置預覽元素後重設
    vs.root.find(".preview").html(vs.imgcode);
    local_reset();
}

其中使用了 local_create_imgcode() 主要是建立圖片編碼,但不直接透過 jQuery 的 append() 或 prepend() 寫入 HTML。這是因為多次插入 DOM 會影響校能,例如有50張圖片那瀏覽器資源不足可能會停擺,所以我們要做的是,在最後一次性的插入DOM。

// 建立圖片
var local_create_imgcode = function(src){
    vs.imgcode += '<img class="img" src="' + src + '">';
}

最後 vs.imgcode 會成為一個不間段的標籤字串,我們才在最後加入 DOM,這樣效能才會好。最後我們還原選取鈕供下一次使用。

// 還原 input[type=file]
var local_reset = function (){
    vs.imgcode = '';
    vs.root.find(".upl").val(null);
}

透過上面 vmodel 結構化 jQuery 與這次的範例目標,整個物件的感覺就更加強烈,維護上也方便許多。關於 vmodel.js 的寫法與文件,可以參考我的 github 或網站 內部相關文章

那麼如果不使用 vmodel 那要如何組織你的 jQuery 呢?我這邊就以 jQuery 建議與修改,做為組織程式碼的方法做為範例。

var Preview = new function (){

    var root = $(".form1");

    // 連續的圖片編碼
    var imgcode = '';

    // 選取發生改變
    this.change_file = function (){
        root.on("change", ".upl", function (){
            show(this);
        });
    }

    // 批次圖片,先清空後再插入
    var show = function (input){
        if (input.files && input.files[0]) {
            clean();
            each_img(input.files);
        }
    }

    // 批次讀取,最後再一次寫入
    var each_img = function (files){

        $.each(files, function (index, file){
            var src = URL.createObjectURL(file);
            create_imgcode(src);
        });

        // 放置預覽元素後重設
        root.find(".preview").html(imgcode);
        reset();
    }


    // 建立圖片
    var create_imgcode = function(src){
        imgcode += '<img class="img" src="' + src + '">';
    }
    

    // 清空預覽區域
    var clean = function (){
        root.find(".preview").empty();
    }

    // 還原 input[type=file]
    var reset = function (){
        imgcode = '';
        root.find(".upl").val(null);
    }
}

// 執行
Preview.change_file();

當然相較之下,會是使用 vmodel 的可塑性高出許多囉!

 


Comments

  1. 您好,

    讀完本文,有問題無法理解,
    問題如下:

    1)
    前端使用上預覽圖片沒有問題,但是….
    似乎因為第38行執行local_reset()後,要上傳的資料被清掉了
    如:http://www.chit.com.tw/zSelect.asp

    2)
    如果不執行local_reset(),註解掉以後
    若重新選擇圖片,圖片會一直堆疊無法清除
    按了reset鍵,input會清除,重選圖片後圖片依然會繼續增加

    找多圖片上傳前預覽的方法有好多天了
    您的方法是其中最喜愛的
    若您時間允許,是不可以協助指點呢?
    打擾您了~~

    萬分感謝~~

    • 最近很少更新這裡現在才發現好多人留言XD
      基本上這邊介紹的就是基礎應用。如果要搭配上傳,只要稍作變化就可以了,我自己連接後端上傳沒有問題喔~
      可以再思考一下如何改變。local_reset()是必要的喔,不使用會堆疊是正常的。
      謝謝你的認同,很感謝你~~~

      • 謝謝您
        我沒有想到解決辦法
        最後加了一個按鈕
        請User按那個按鈕將頁面reload重新選擇圖片^^
        自己該用心學習jQuery了
        再次感謝~~

        • 你是說顯示區域的圖片堆疊嗎?突然意會~
          如果是的話,使用 $(selector).html(”); 看看

  2. 不好意思,想請問”未選擇任何檔案”是否有方法可以移除?
    若您方便回答,再請您幫忙了,謝謝!:)

  3. 請問~如果我對每張圖做一個小叉叉,點了可以刪除單一圖片,這樣要怎麼寫比較好呢@@?
    我用jquery做click沒有效果…想請原po指點一下~感謝~

    • 這個算蠻基本的呢,基礎要再反覆練習一下。Google 搜尋 jquery 的 on() 相關教學試試

  4. 您好~看到原po的方法覺得很實用
    不過目前套用在本人專案上時卻發生想不通的問題
    我是使用您下面非vmodel的方法
    執行到 // 批次圖片,先清空後再插入 這段程式碼時就沒有執行成功了
    圖片的預覽也沒有出現 卡好久還是想不通
    想請教這會是什麼問題呢?感謝~

    • 嗯…這樣的話我也不是很懂,有相關的程式碼片段嗎?

      • 早安你好,以下為我的程式碼片段
        在JS的部分段落中我有加入alert()函數來判斷有沒有執行到
        不過卻只有在進入此頁面時跳出alert(‘change’)
        選取檔案後除了有顯示選擇的檔案名稱 圖片預覽並未出現
        會是我撰寫的部分有錯誤或是少了什麼嗎?
        (不好意思有點長,有空方便回答的話,再請您幫忙了,謝謝!)
        ————————————-
        //連續顯示上傳圖片
        var Preview = new function (){
        var root = $(“.text-center”);
        // 連續的圖片編碼
        var imgcode = ”;
        // 選取發生改變
        this.change_file = function (){
        root.on(“change”, “.userfile”, function (){
        show(this);
        alert(this);
        });
        alert(‘change’);
        }
        // 批次圖片,先清空後再插入
        var show = function (input){
        alert(‘show’);
        if (input.files && input.files[0]) {
        //clean();
        alert(‘show2’)
        each_img(input.files);
        }
        }
        // 批次讀取,最後再一次寫入
        var each_img = function (files){
        $.each(files, function (index, file){
        var src = URL.createObjectURL(file);
        alert(src);
        create_imgcode(src);
        });
        // 放置預覽元素後重設
        root.find(“.preview”).html(imgcode);
        alert(‘put’);
        //reset();
        }
        // 建立圖片
        var create_imgcode = function(src){
        imgcode += ”;
        alert(imgcode);
        }
        }
        // 執行
        Preview.change_file();
        ———————————————–

  5. 不好意思 請問這種方法可以再加以寫入PHP 的BLOB類型嗎

    • 你想要的是 PHP 透過 Blob 讀取伺服器資料夾的圖片嗎?我想網路有很多資源你先找找,等我有時間再寫。

發表迴響