前端圖片預覽

liuyueershihao發表於2017-11-10

很多時候我們都會有圖片上傳的功能需求,如果我們先將圖片上傳到伺服器,然後在將返回結果顯示在前端,這樣的操作效能開銷太大,如果圖片一多,簡直要哭,而且萬一還碰到了上傳錯誤要刪除的,那簡直無法想象了。所以我們需要先將圖片在前端展示後,然後由使用者確認沒有問題了,再統一上傳,這樣才是比較理想的。

input

type=file

相信大家都知道,要在前端實現圖片的上傳,我們離不開的是一個 <input> type=file 的 input 元素,該元素可以允許使用者選擇一個或者多個檔案。

    <input type="file">複製程式碼

此時,我們點選 input 元素,就可以瀏覽本地檔案並選擇上傳。但是,此時我們只能選擇一個檔案,而不能多個。這時就需要 <input> 標籤的另一個屬性 multiple

multiple

multipla 屬性允許使用者選擇多個檔案,他是一個不需要值的屬性,也就是說,只要你的 input 標籤上出現了這個屬性,那麼不論其值是什麼,他都會支援多檔案選擇。通常來說我們使用 multiple 只會使用其屬性名,而不會給他加值

    <input type='file' multiple>複製程式碼

accept

如果你嘗試了以上標籤及屬性,你會發現你的不但能選擇 image 檔案,還能選擇其他各種各樣的檔案。但是一般來說對我們有需要的只是 image 檔案,至於其他什麼的,愛咋咋地吧,只要不出來妨礙我就可以了。所以這時候我們需要 accept 的屬性來進行限制。accept 屬性接受逗號分隔的 MIME 型別字串:

1. accept='image/png' 或者 accept='.png' --只接受 .png 格式的圖片
2. accept='iamge/png,image/jpeg' 或者 accept='.png, .jpg .jpeg' 接受 .png .jpeg .jpg 格式的圖片
3. accept='image/*' 接受所有型別的 image 


    <input type='file' multiple accetp='image/*'>複製程式碼

注: 'image/*' 在部分瀏覽器中(Chrome和Safari等Webkit瀏覽器)響應比較緩慢,可以用以下方法代替

    <<input type="file" multiple accept='image/png, image/jpeg, image/jpg, image/svg, image/gif'> 複製程式碼

樣式

一般來說我們都會將 input 設定為 display:none, 然後通過 label 來設定其顯示樣式

    // css

    input{
        display:none;
    }
    label{
        // 關於label樣式
    }

    // html

    <input type='file' multiple accept='image/png, image/jpeg, image/jpg, image/svg, image/gif' id='inputFile'>
    <label for="inputFile">上傳圖片</label>複製程式碼

FileList 物件

選中檔案通過 HTMLInputElement.files 屬性返回了一個 FileList 物件,這個物件是一個包含了許多 file 檔案的列表。每個 file 物件包含了一下資訊:

1. name:檔名
2. lastModified:檔案最後一次修改時間(時間戳形式)
3. lastModifiedDate:檔案最後一次修改時間(UNIX timestamp形式)
4. size: 檔案大小(byte 為單位)
5. type:檔案 MIME 型別複製程式碼

我們可以通過對 input 標籤監聽 change 事件:

    //  js

    document.getElementById('inputFile').addEventListener('change', changeHandler, false);

    function changeHandler(e) {
        var files = e.target.files;
        console.log(files) // 這裡我們能獲取到所選擇的檔案資訊,需要注意的一點是 files 是個類陣列物件。
    } 複製程式碼

FileReader or 物件 URL

當我們獲取到檔案物件資訊 files 了以後,我們要如何將他在頁面上預覽出來,這裡提供了兩種方法:FileReader 或者 物件 URL。
這兩種方法該如何使用,又有何區別呢?

1. FileReader

FileReader 實現了一種非同步的讀取機制。他必須先通過 FileReader() 建構函式建立出一個 fileReader 例項。該例項實現了一下幾個方法和事件(部分):

  1. readerAsDataURL(file): 讀取檔案並以資料 URI 形式儲存在 result 屬性中

  2. load 事件:在檔案載入成功後觸發 load 事件

  3. error 事件:在檔案載入失敗後觸發 error 事件

  4. progress 事件:在讀取檔案的過程中觸發 progress 事件,該事件可以近似(間隔性觸發,不是實時響應)監聽檔案上傳進度。該方法有三個屬性:lengthComputable(進度資訊是否可用), loaded(已經載入了多少), total總共有多少。

usage:

    files.forEach(function(item) {
        var reader = new FileReader();
        reader.readAsDataURL(item);
        reader.onprogress = function(e) {
            if (e.lengthComputable) {
                // 簡單把進度資訊列印到控制檯吧
                console.log(e.loaded / e.total + '%') 
            }
        }
        reader.onload = function(e) {
            var image = new Image()
            image.src = e.target.result
            body.appendChild(image)
        }
        reader.onerror = function(e) {
            console.log('there is an error!')
        }
    })複製程式碼

2. 物件 URL

物件 URL 指的是引用儲存在 File 或 Blob 中的資料 URL。使用物件 URL 的時候不用像 FIleReader 一樣要先把資料讀取到 JavaScript 中,他可以引用 記憶體中 URL 地址而使用。

建立物件 URL 方法: window.URL.createObjectURL()。相容寫法:

    function creatObjectURL(file) {
        if (window.URL) {
            return window.URL.createObjectURL(file);
        } else if (window.webkitURL) {
            return window.webkitURL.createObjectURL(file);
        } else {
            return null
        }
    }複製程式碼

usage:

    files.forEach(function(item) {
        var url = createObjectURL(item)
        var image = new Image()
        image.src = url
        body.appendChild(image)
    })複製程式碼

區別:

參(zhao)考(chao)

  1. FileReader 是非同步操作,而物件 URL 是同步操作

  2. FileReader.readAsDataURL 返回的是一個包含更多位元組的 base64 格式,createObejctURL 返回的是一個帶 hash 的 URL。

  3. 由於兩者返回形式不同,FileReader.readerAsDataURL 會佔用更多記憶體,但是當你不再使用他的時候,他會自動釋放記憶體,而 createObjectURL 則只有當你的頁面關閉或者手動呼叫 revokeObejctURL 的時候才能釋放記憶體。

  4. 從相容性來說: createObjectURL 和 FileReader.readerAsDataURL 都相容 IE10+ 和現代所有主流瀏覽器

  5. createObjectURL 相對 FileReader.readerAsDataURL,效率較高。但是如果圖片較多,則最好手動清除記憶體,可以把 URL 當做引數直接傳給 window.URL.revokeObjectURL()。相容寫法:

     function revokeObjectURL(url) {
         if (widnow.URL) {
             return window.URL.revokeObjectURL(url)
         } else {
             return window.webkitURL.revokeObjectURL(url)
         }
     }複製程式碼

簡單實現:

    // css

    input{
        display:none;
    }
    label{
        // 關於label樣式
    }

    // html

    <input type='file' multiple accept='image/png, image/jpeg, image/jpg, image/svg, image/gif' id='inputFile'>
    <label for="inputFile">上傳圖片</label>

    // js 

    var inputFile = document.getElementById('inputFile')
    var body = document.body || document.getElementsByTagName('body')[0]

    inputFile.addEventListener('change', changeHandler, false)

    function changeHandler(e) {
        var files = Array.from(e.target.files)
        files.forEach(function(item) {
            var image = new Image()
            image.src = createObjectURL(item)
            body.appendChild(image)
            image.onload = function() {
                revokeObjectURL(this.src)
            }
        })
    }

    function createObjectURL(file) {
        if (window.URL) {
            return window.URL.createObjectURL(file)
        } else {
            return window.webkitURL.createObjectURL(file)
        }
    }

    function revokeObjectURL(file) {
        if (window.URL) {
            return window.URL.revokeObjectURL(file)
        } else {
            return window.webkitURL.revokeObjectURL(file)
        }
    }複製程式碼

相關文章