使用HTML 5 File API(1)

by vivid 18. 七月 2012 05:35

.NET Magazine國際中文電子雜誌
作 者:許薰尹
審 稿:張智凱
文章編號:N120712602
出刊日期:2012/7/18 (週三)

本文將介紹HTML 5中提供的File API,因為不同的瀏覽器對於HTML 5的支援程度不同,因此文中的範例以Chrome進行測試,但程式碼不見得可以在其它瀏覽器中正確的執行。

為了安全性的理由,JavaScript並沒有定義任何應用程式介面可以存取用戶端硬碟中的檔案,不過在HTML 5,增加了File API,可以用來查詢、讀取或者寫入檔案的內容。

File API中包含了許多JavaScript 物件,例如File與Blob,讓程式設計師可以透過它們來存取檔案。常用的物件:

  • File:可以用來存取硬碟中的檔案。
  • Blob:表示儲存在檔案中的原生資料。
  • FileList:File物件所成的集合。
  • FileReader:用來讀取檔案。

 

使用HTML 5 File物件

HTML 5提供許多物件可以存取檔案,第一個要介紹的物件是File。File物件是不可變的,它包含了許多屬性與方法,可用來存取檔案的資訊。其中Name屬性中包含檔案的名稱;size屬性記錄檔案的大小;lastModifiedDate屬性則儲存檔案最後修改時間。參考以下範例程式碼。

<!DOCTYPE html >
<html xmlns = "http://www.w3.org/1999/xhtml" >
<head>
    <title> </title>
    <script type = "text/javascript" >
        function ProcessFile( e ) {
            var file = document.getElementById('file').files[0];
            if (file) {
                document.getElementById('result').innerHTML =
                    "檔案名稱 :" + file.name + " <br/>  " +
                    "&nbsp;&nbsp;&nbsp;&nbsp;檔案大小 :" + file.size + "<br/>   " +
                    "&nbsp;&nbsp;&nbsp;&nbsp;最後修改時間 :" + file.lastModifiedDate;
            }
        }
        function contentLoaded () {
            document.getElementById('file').addEventListener('change' ,
ProcessFile , false );
        }
        window.addEventListener( "DOMContentLoaded", contentLoaded , false );
    </script>
</head>
<body>
    <input type = "file" id = "file" name = "file"  />
    <div id = "result" > </div>
</body>
</html>

範例中使用window的addEventListener方法註冊了DOMContentLoaded事件,此事件是在目前的網頁DOM內容(包含HTML、JavaScript,但不包含Image或CSS)載入完成後觸發。DOMContentLoaded事件發生時,將會執行contentLoaded方法,我們在此方法中註冊了型別為file的INPUT項目change事件 只要選取的檔案變動時,會自動執行ProcessFile方法。

在ProcessFile方法中,先取得INPUT項目中選取的檔案,然後讀取name、size與lastModifiedDate屬性顯示在畫面上。

參考下圖是上述範例在Chrome瀏覽器上執行的結果。

clip_image002

圖 1:使用File物件讀取檔案的屬性。

 

使用HTML 5 FileList物件

FileList物件是由一堆File物件所成的集合,你可以從FileList的length屬性取得檔案的數量。要取得FileList中的File物件,可以使用item方法,傳入檔案索引即可。參考以下範例,Input (file)標籤中設定了multiple attribute,可以一次傳送多個檔案(不一定每一個瀏覽器都有支援這個attribute)。

<!DOCTYPE html >
<html xmlns = "http://www.w3.org/1999/xhtml" >
<head>
    <title> </title>
    <script type = "text/javascript" >
        function ProcessFile( e ) {
            var files = e.target.files;
            for (var i = 0; i < files.length ; i++) {
                document.getElementById('result').innerHTML += "共" + files.length + "個檔案";
                document.getElementById('result').innerHTML += " <br/>" +
                    "檔案名稱 :" + files.item(i).name + " <br/>  " +
                    "&nbsp;&nbsp;&nbsp;&nbsp;檔案大小 :" + files.item(i).size + "<br/>   " +
                    "&nbsp;&nbsp;&nbsp;&nbsp;最後修改時間 :" + files.item(i).lastModifiedDate;
            }
        }
        function contentLoaded () {
            document.getElementById('file').addEventListener('change' ,
ProcessFile , false );
        }
        window.addEventListener( "DOMContentLoaded", contentLoaded , false );
    </script>
</head>
<body>
    <input type = "file" id = "file" name = "file" multiple = "multiple" />
    <div id = "result"> </div>
</body>
</html>

我們在ProcessFile方法中利用e.target.files取得FileList物件,再利用for迴圈將FileList物件中各個File物件的name、size與lastModifiedDate屬性顯示在畫面上。執行的結果參考圖2所示。

clip_image004

圖 2:使用FileList物件。

判斷是否支援File API

要確保程式可以正確的運作,您需要先檢查瀏覽器是否支援File API的功能,建議在程式中加入一些程式,來進行檢查,修改上個範例的程式碼如下:

<!DOCTYPE html>
<html xmlns = "http://www.w3.org/1999/xhtml" >
<head>
    <title> </title>
    <script type = "text/javascript" >
        function ProcessFile( e ) {

            if ( window.File && window.FileReader && window.FileList && window.Blob ) {
                // 瀏覽器支援所有的 File API
            } else {
                alert('您的瀏覽器並不支援 File API');
                return;
            }

            alert("get files");
            var files = e.target.files;
            for ( var i = 0; i < files.length ; i++ ) {
                document.getElementById('result').innerHTML += "共" + files.length +"個檔案";
                document.getElementById('result').innerHTML += " <br/>" +
                    "檔案名稱 :" + files.item(i).name + " <br/>  " +
                    "&nbsp;&nbsp;&nbsp;&nbsp;檔案大小 :" + files.item(i).size + "<br/>   " +
                    "&nbsp;&nbsp;&nbsp;&nbsp;最後修改時間 :" + files.item(i).lastModifiedDate;
            }
        }
        function contentLoaded () {
            document.getElementById('file').addEventListener('change' ,
ProcessFile , false );
        }
        window.addEventListener( "DOMContentLoaded" , contentLoaded , false);
    </script>
</head>
<body>
    <input type = "file" id = "file" name = "file" multiple = "multiple" />
    <div id = "result"> </div>
</body>
</html>

 

以下是ie9瀏覽器的測試結果,因為ie9不支援multiple的attribute,因此當你按下畫面中的「Browse」按鈕時,只能夠選取一個檔案,然後會顯示不支援File API的訊息,請參考圖3所示:

clip_image006

圖 3:使用JavaScript判斷是否支援File API。

使用拖曳方式載入檔案

另一個載入檔案的方式是透過拖曳方式(Drag-and-Drop)來達成。你可以從檔案總管選取一或多個檔案,然後利用滑鼠拖曳到網頁之中。參考以下範例,使用一個名為「dropDiv」的div標籤進行拖曳的設計。

Div設定了dropzone的屬性,這個屬性可以設定成三個值:

  • Copy:複製拖曳的資料。
  • Move:移動拖曳的資料。
  • Link:建立連結。

參考以下使用拖曳方式載入檔案的範例程式:

<!doctype html >
<html>
<head>
    <title> Drag-and-Drop API</title>
    <script type = "text/javascript" >
        function wireDragEvents () {     
            var div = document.getElementById('dropDiv');
            div.addEventListener( 'drop', handleDrop, false );
            div.addEventListener( 'dragenter', handleEnter, false );      
            div.addEventListener( 'dragover', handleDragOver , false );
        }
             
        function handleEnter( e ) {
            document.getElementById('dropDiv').textContent = "";
            e.stopPropagation();
            e.preventDefault();
        }
        function handleDrop( e ) {
            e.stopPropagation();
            e.preventDefault();
            var files = e.dataTransfer.files;
            for (var i = 0 ; i < files.length ; i++) {
             
                document.getElementById('dropDiv').innerHTML += " <br/>" +
                    "檔案名稱 :" + files.item(i).name + " <br/>  " +
                    "&nbsp;&nbsp;&nbsp;&nbsp;檔案大小 :" + files.item(i).size + "<br/>   " +
                    "&nbsp;&nbsp;&nbsp;&nbsp;最後修改時間 :" + files.item(i).lastModifiedDate;
            }
        }

        function handleDragOver( e ) {       
            e.stopPropagation();
            e.preventDefault();
            e.dataTransfer.dropEffect = 'copy';
        }

        window.addEventListener( "DOMContentLoaded" , wireDragEvents , false );
    </script>
</head>
<body>
   
    <div id = "dropDiv" dropzone = "copy" style = "width: 300px; height: 200px; padding: 50px;
        border: solid 2px black;" >
        請拖曳檔案到此區塊 </div> 
</body>
</html>

DnD API支援許多拖曳動作進行時會觸發的事件,常用的事件包含:

  • dragstart:開始拖曳的動作。
  • drag:拖曳過程中發生。
  • dragenter:將資料拖曳進入drop zone時發生。
  • dragover:將資料拖曳到drop zone上方時發生。
  • drop:將資料拖曳進入drop zone後放開滑鼠時發生。

範例中在wireDragEvents方法中註冊drop、dragenter與dragover事件。Dragenter事件發生時,將執行handleEnter方法,方法中先將顯示執行結果的div內容清空,接著使用e.stopPropagation()方法防止事件往父層傳遞;e.preventDefault ()方法避免執行瀏覽器預設的行為。

drop事件發生時,將執行handleDrop方法,在此方法中可以利用e.dataTransfer來取得dataTransfer物件。dataTransfer物件包含拖曳的資料,我們可以從e.dataTransfer.files取得從檔案總管拖曳到瀏覽器中的檔案。

dragover事件發生時,將執行handleDragOver方法,在此方法中設定了dataTransfer物件的dropEffect屬性,它用來控制要執行的作業是copy、move、或是link。

範例執行的結果請參考圖4所示:

clip_image008

圖 4:使用拖曳方式存取檔案。

使用拖曳方式上傳檔案到伺服器

若想要將檔案總管拖曳的檔案透過瀏覽器直接上傳到伺服器,則可以利用FormData物件,例如以下範例,在handleDrop方法中,建立FormData物件,並將拖曳到瀏覽器的第一個檔案附加到FormData,參考以下範例程式碼:

<!doctype html>
<html>
<head>
    <title> Using the Drag-and-Drop API </title>
    <script type = "text/javascript">
        function wireDragEvents( ) {
            var div = document.getElementById('dropDiv');
            div.addEventListener( 'drop', handleDrop, false );
            div.addEventListener( 'dragenter', handleEnter, false );      
            div.addEventListener( 'dragover', handleDragOver, false );
        }
        function handleEnter( e ) {
            document.getElementById('dropDiv').textContent = "";
            e.stopPropagation();
            e.preventDefault();
        }
        function handleDrop( e ) {
            e.stopPropagation();
            e.preventDefault();
            var files = e.dataTransfer.files;
            var file = files[0];
            document.getElementById('dropDiv').innerHTML += " <br/>" +
                    "檔案名稱 :" + file.name + " <br/>  " +
                    "&nbsp;&nbsp;&nbsp;&nbsp;檔案大小 :" + file.size + "<br/>   " +
                    "&nbsp;&nbsp;&nbsp;&nbsp;最後修改時間 :" + file.lastModifiedDate;

            var formData = new window.FormData();
            formData.append( 'myile', file );

            var xhr = new XMLHttpRequest();
            xhr.open( 'POST', 'upload.aspx' );
            xhr.onload = function () {
                if (xhr.status === 200) {
                    alert('檔案上載成功!!');
                } else {
                    alert('檔案上載失敗!!');
                }
            };
            xhr.send(formData);
         
        }
        function handleDragOver( e ) {       
            e.stopPropagation();
            e.preventDefault();
            e.dataTransfer.dropEffect = 'copy';
        }

        window.addEventListener( "DOMContentLoaded" , wireDragEvents , false );
    </script>
</head>
<body>
   
    <div id = "dropDiv" dropzone = "copy" style = "width: 300px; height: 200px; padding: 50px;
        border: solid 2px black;" >
        請拖曳檔案到此區塊 </div>
</body>
</html>

FormData的append方法傳入兩個參數:識別名稱,與要上傳的檔案。識別名稱在本範例中命名為「myfile」,將來在伺服端可以利用此名稱來取得上傳的檔案。接著我們利用XMLHttpRequest物件發出一個HTTP POST請求,叫用XMLHttpRequest物件的send方法將檔案透過FormData傳送到伺服端的upload.aspx程式進行處理。

伺服端的upload.aspx檔案是一個ASP.NET的網頁,接收到檔案之後,透過「myFile」識別名稱取出檔案資訊,然後利用HttpPostedFile物件的SaveAs方法,將檔案儲存到伺服端的upload目錄中。

<%@ Page Language = "C#" %>
<%   
    if ( Request.Files != null )
    {
   var f=Request.Files ["myfile"].FileName ;
        Request.Files [ "myfile" ].SaveAs( Server.MapPath( @"~\upload\" ) + f );
    }
%>

Tags:

.NET Magazine國際中文電子雜誌 | HTML | 許薰尹Vivid Hsu

新增評論




  Country flag
biuquote
  • 評論
  • 線上預覽
Loading






NET Magazine國際中文電子雜誌

NET Magazine國際中文電子版雜誌,由恆逸資訊創立於2000,自發刊日起迄今已發行超過500篇.NET相關技術文章,擁有超過40000名註冊讀者群。NET Magazine國際中文電子版雜誌希望藉於電子雜誌與NET Developer達到共同學習與技術新知分享,歡迎每一位對.NET 技術有興趣的朋友們多多支持本雜誌,讓作者群們可以有持續性的動力繼續爬文。<請加入免費訂閱>

月分類Month List