HTML5的drag and drop機制,不僅可以接收從其他應用軟體拖曳過來的資料,他其實還可以讀取檔案。讀取檔案,靠的是File API。
還是先看一下File API的介面:
DataTransfer.files物件,其實是一個FileList,透過他的length屬性可以知道裡面是否有File物件,然後可以用類似陣列的方式來取得個別的File物件。
取出來的每個File物件,繼承自Blob物件:
interface Blob {
readonly attribute unsigned long long size;
readonly attribute DOMString type;
//slice Blob into byte-ranged chunks
Blob slice(in unsigned long long start,
in unsigned long long length,
optional DOMString contentType);
};
可以透過size取得檔案長度,透過type取得檔案的mime type。透過slice()方法,則可以傳回部份的檔案。
另外,File物件定義了幾個屬性:
interface File : Blob {
readonly attribute DOMString name;
readonly attribute DOMString lastModifiedDate;
};
name就是檔案名稱,lastModifiedDate則可取得最後修改的日期資訊。
要讀取檔案,還需要使用FileReader物件:
[Constructor]
interface FileReader {
// async read methods
void readAsArrayBuffer(in Blob blob);
void readAsBinaryString(in Blob blob);
void readAsText(in Blob blob, [Optional] in DOMString encoding);
void readAsDataURL(in Blob blob);
void abort();
// states
const unsigned short EMPTY = 0;
const unsigned short LOADING = 1;
const unsigned short DONE = 2;
readonly attribute unsigned short readyState;
// File or Blob data
readonly attribute any result;
readonly attribute FileError error;
// event handler attributes
attribute Function onloadstart;
attribute Function onprogress;
attribute Function onload;
attribute Function onabort;
attribute Function onerror;
attribute Function onloadend;
};
FileReader implements EventTarget;
雖然有這麼長一絡,其實只要知道了他「非同步運作」的機制,就可以簡單地使用:
var reader = new FileReader();//產生FileReader物件實例
reader.addEventListener('load', function(){
console.log(this.result);//指定事件處理函數來處理讀取的資料
}, false);
reader.readAsDataURL(File);//將檔案用不同格式讀取
readAsArrayBuffer(), readAsBinaryString(), readAsText(), readAsDataURL()可以選擇讀取檔案後,存放在result屬性裡的資料格式。load事件會在檔案讀取完畢時觸發,這時就可以在事件處理函數內,透過result屬性來取得讀取的資料。如果是圖檔,有支援DataURL的瀏覽器,就可以直接把DataURL指派為img元素的src,然後在網頁中呈現。JavaScript字串的內部格式都是Unicode,所以readAdText會影響到一些binary資料的正確性。readAsBinaryString應該就沒有這個問題,不過目前找不到比較好的應用方式...其實如果瀏覽器實作了XMLHttpRequest2規格,就用得上了。目前的XMLHttpRequest在傳送資料時會把格式轉成UTF8,XMLHttpRequest2則有能力使用Binary格式來傳送資料。
另外,如果是在網頁之外的環境,例如WebWorker中,也可以使用「同步」版本的FileAPI,不過我還沒嘗試過,也不確定目前支援WebWorker的瀏覽器,在WebWorker的Global物件中是否有提供FileReader。
如果對整合FileAPI的應用有興趣,我試作了一個使用AJAX的方式上傳檔案的網頁,因為目前的XMLHttpRequest不支援Binary格式資料的傳送,所以我是用readAsDataURL()來取得base64編碼的資料,然後利用XMLHttpRequest實作一個multipart/form-data格式的POST命令來傳送檔案資料(一般是使用url encoded格式的POST)。缺點是,伺服器收到的資料也還是base64編碼,所以伺服器端需要對檔案做解碼。有興趣可以看一下我前不久寫的文章:HTML5 Drag&Drop + 純AJAX檔案上傳,程式我就不在這裡貼了。