【javascript】File API 讀取並顯示本地圖片 -1

在過去,瀏覽器是不允許我們讀取本地的文件,包括圖片。因此,當我們需要預覽一個圖片的時候,往往先將它傳送到服務端,然後服務端返回一個訪問url地址,達到預覽圖片的功能。而如今,隨着標準不斷的改善,javascript裏的API越來越多,我們可以通過直接讀取本地文件的方式,加載我們想要看到的文本或者圖片,一定程度上減少了服務端的壓力。
接下來我們說一下運行環境和使用的庫:
1. chrome 瀏覽器
2. zepto.js
其實在之前,我們有介紹過javascript裏的 File 對象,它只能通過 input[type=file]元素獲取。當用戶觸發了文件控件的onclick事件,同時選擇了某一個文件確認時,觸發文件控件的onchange事件。

一 : 案例頁面結構

<body>
    <article>
        <header id="header"><h1>文件上傳</h1></header>
        <section class="box">
        <form id="form" enctype='multipart/form-data' method='post'   action='#'>
            <div class="upload-label"><h2>請選擇文件</h2></div>
            <div class="upload-box add-button"></div>
            <button class="upload-button" type="submit">上傳</button>
            <input type="file" name="files"  id="files" >
        </form>
        </section>

    </article>
</body>

主要關注的一點是<input type="file" name="files" id="files" > 我們這個form表單中存在這一個input[type=file]的元素,只有通過這一個接口,我們纔可已調用電腦的文件管理器資源。因此很多時候我們會重寫打開文件的界面,因爲原生的界面的確不符合我們的審美。
下面是一張我寫的一個簡單的界面,相比之前有了很好的改善。
這裏寫圖片描述

這是在手機上的一個顯示,在這裏我們就不糾結css樣式了。直接看重點吧。

二: 通過zepto庫綁定dom元素的事件

$(function(){
  var maxlen = 5,    //文件可讀取最大長度
      filelist = [],    //存放 File對象數組
      index = 0;    //當前存放File的長度


  // 按鈕元素監聽click事件,執行add方法
  $(".add-button").bind('click',add);

  // input[type=file]元素監聽change事件 執行filter方法
  $("#files").bind('change',filter);

  // 模擬input[type=file]添加文件的動作
  function add(e){
    if (index < maxlen ){ 
        $("#files").trigger("click");
    }else{
        // 超過了選中文件文件的限制
        // AnimateUtils.info('超過最大上傳限制');
    }
 }
  //  過濾文件類型    
  function filter(e){
    var files = e.target.files,   // 獲取本地文件File對象集合
        typelist = ["image/jpeg","image/png"],  // 合法文件type列表
        filetype, // 當前文件的類型
        isPic;  // 文件類型是否合法標識

    //  針對PC端多選的情況,前提是 input[type=file]元素包含 multiple屬性

    for (var i =0,len=files.length; i <len && index < 5 ;i++){
        filetype  = files[i].type;
        //判斷filetype是否屬於圖片
        isPic= typelist.some(function(item){return item == filetype;});
        if(isPic){
            filelist.push(files[i]);  //添加至文件列表
            addImage(files[i]);   //同時添加到頁面
            index++;
        }else{
           // 提示文件非圖片
           // AnimateUtils.info('不支持的文件類型');
        }
    }
}
});

這裏可能寫的比較亂,我們來好好梳理一下,首先當用戶點擊了<div class="upload-box add-button"></div>元素時,它會trigger(觸發 ) <input type="file" name="files" id="files" > 的click事件。這麼一來,就好像是點擊了”文件控件”的按鈕一樣。

這裏寫圖片描述

三:重點 : 理解File類型
我們是如何獲取到File類型的實例的呢 ? 答案就是在 <input type="file" name="files" id="files" > 這個元素裏。一旦你綁定了它的onchange事件,它會將你選中的文件進行封裝成一個File類型的實例。存放在目標元素的files引用中,接下來我們可以來看看這個對象實例到底是怎麼樣的。
這裏寫圖片描述

原來files引用的是一個FileList集合,裏面存放了File類型的數據, 每一個File類型都包含以下5個主要的參數。

  1. lastModified #保存的是一個時間戳,表示文件最後修改的時間
  2. lastModifiedDate #保存的是一個Date類型,是時間戳翻譯的格式
  3. name #保存的是我們選擇的文件的名稱
  4. size #文件的大小 單位是字節
  5. type #文件的類型 如果沒有特定的類型,比如說文本文件,一般爲空字符串

其實我們截圖中,還可以發現File類型是繼承Blob類型的,因此文件可以通過文本的方式進行讀取。在我們上面的代碼中,其實有一個addImage方法並沒有介紹,其實它主要就是將這個指向本地文件的引用對象通過某種方式讀取到瀏覽器中。這種方式就是借用了第二要說的類型,FileReader類型。

四 : 介紹FileReader類型
我們都知道一個文件要是很大的話,直接讀取會導致操作阻塞,必須得要等文件讀取完後纔可以進行其他操作。而FileReader類型實現的就是異步文件讀取機制。書上說,可以把它當做XMLHttpRequest對象,區別在於它面向的是本地文件系統,而XMLHttpRequest是面向遠程服務器。

接下來我們來繼續完善上面的例子,把FileReader類型的對象實例創建出來,然後對File類型的實例操作。

// 將圖片加載至瀏覽器中,並且顯示出來
function addImage(file){

    // 創建 一個FileReader對象
    var reader = new FileReader();
    // 讀取文件作爲URL可訪問地址
    reader.readAsDataURL(file);

    // 監控讀取加載過程 
    reader.onprogress =function(e){

    // 是否支持獲取當前讀取文件長度
        if(e.lengthComputable){

            // 將新創建的一個展示區域元素存放在 reader.uploadBox 中
            reader.uploadBox= reader.uploadBox || $('<div class="upload-box"></div>').insertBefore('.add-button');
            // 讀取進度顯示
            reader.uploadBox.text( Math.ceil((e.loaded / e.total) * 100) +"%");

        }

    //異步加載文件成功
    reader.onload = function(e){
       // this 對象爲reader
       // reader.result 表示圖片地址
       reader.uploadBox.wrapInner('<img style="width:100%;height:100%; padding:.2rem;" src="'+reader.result+'" alt="'+file.name+'" />');
    }

}

這裏發現我們只是使用了FileReader類型裏的 readAsDataURL方法,還有onprogress事件和onload事件。很明顯,想要學習FileReader類型,這個例子還是不夠的。接下來我們來看看FileReader類型可以使用的方法和監聽事件。
可用方法:
1. readAsText(file,encoding) 將以純文本的形式讀取文件,可以指定編碼類型,結果保存在result屬性中。
2. readAsDataURL(file) 讀取文件並將文件以數據URI的方式保存在result屬性中。
3. readAsBinaryString(file) 讀取文件並將一個字符串保存在result屬性中,其中一個字符表示一個字節。
4. readAsArrayBuffer(file) 讀取文件並將一個包含文件內容的ArrayBuffer保存在result屬性中。

可用事件:

  1. error事件
    讀取失敗後觸發, 失敗的原因可能爲 未找到文件,安全性錯誤,讀取中斷,文件不可讀,編碼錯誤.錯誤都會以數字的形式存放在code屬性中。
  2. progress事件
    正在讀取中觸發,每50ms觸發一次 其中event對象會包含 lengthComputable , loaded , total 屬性。分別表示是否可以計算長度,當前加載長度和總共需要加載長度。
  3. load事件 讀取完畢後觸發

error事件和load事件是不可以同時觸發的,因爲完成與完成失敗是完全對立的結果。
我們來看一下加載圖片成功後顯示的頁面吧

這裏寫圖片描述

當我繼續添加文件的時候,js判斷超過了顯示的長度,禁止文件繼續添加。不過我們可以看到圖片確實被加載到瀏覽器中,我們來看看這個img元素的src屬性是什麼樣子的。

這裏寫圖片描述

可以看到本地URI地址的前綴含有data:image/jpeg;的標示,可以告知瀏覽器這個地址是一個圖片的格式。可以用圖片的方式進行渲染。所以不能和其它方法進行換用。

五 : 使用另外一種方式加載URL地址
其實還有另外的一種方式可以直接讀取URL地址,而這種方法就是直接丟給瀏覽器去渲染。我們只要調用方法就可以了。詳細的代碼如下

  //創建對象的 URL資源
  function createObjectURL(blob){
        if(window.URL){
            return window.URL.createObjectURL(blob);
        }else if(window.webkitURL){
            return window.webkitURL.createObjectURL(blob);
        }
            return null;
    }
   //銷燬對象的URL資源
    function revokeObjectURL(url){
        if(window.URL){
            return window.URL.revokeObjectURL(blob);
        }else if(window.webkitURL){
            return window.webkitURL.revokeObjectURL(blob);
        }
    }

這種使用方式的存在,我們就不需要什麼註冊監聽事件了,直接調用一個方法就可以將File類型的文件讀取,並且返回一個URL地址.我們修改一下addImage方法。


function addImage(file){

    function createObjectURL(blob){
        if(window.URL){
            return window.URL.createObjectURL(blob);
        }else if(window.webkitURL){
            return window.webkitURL.createObjectURL(blob);
        }
            return null;
    }
    function revokeObjectURL(url){
        if(window.URL){
            return window.URL.revokeObjectURL(blob);
        }else if(window.webkitURL){
            return window.webkitURL.revokeObjectURL(blob);
        }
    }
    $('<div class="upload-box"></div>').wrapInner('<img style="width:100%;height:100%; padding:.2rem;" src="'+createObjectURL(file)+'" alt="'+file.name+'" />').insertBefore('.add-button');
}

這裏寫圖片描述

我們看到這個結果後,就知道它的處理方式和我們FileReader方式不一樣了。至於具體的原理呢,目前還未知道,不過肯定的是,使用createObjectURL方法獲取的圖片必須通過revokeObjectURL方法進行回收。否則會佔用瀏覽器系統資源。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章