轉自:javascript
http://kb.cnblogs.com/page/153741/
php
文件上傳是最古老的互聯網操做之一。html
20多年了,它幾乎沒變,仍是原來的樣子:操做麻煩、缺少交互、用戶體驗不佳。在這個新技術突飛猛進的時代,顯得很是落伍。html5
網頁開發者們想了不少辦法,試圖提高文件上傳的功能和操做體驗,在各類 Javascript 庫的基礎上,開發了五花八門的插件。但是,因爲不一樣瀏覽器之間的差別,缺少統一接口,這些插件要麼用起來很麻煩,要麼不能廣泛適用。java
HTML5提供了一系列新的瀏覽器 API,使得文件上傳有可能出現革命性變化。英國程序員 Remy Sharp 總結了這些新的接口,本文在他的文章基礎上,討論如何採用 HTML5 的 API,對文件上傳進行漸進式加強,實現如下功能:jquery
- iframe 上傳
- ajax 上傳
- 進度條
- 文件預覽
- 拖放上傳
爲了對這些功能有一個感性認識,你能夠先看看 Remy Sharp 提供的範例。程序員
雖然這些 API,尚未獲得普遍部署,但它們是將來的潮流。有了它們,代碼就能夠寫得很是優雅簡潔,上面五個功能都能在 20 行之內實現。web
1、傳統形式ajax
讓咱們從最基本的開始。api
文件上傳的傳統形式,是使用表單元素 file:
<form action="upload.php" method="post" enctype="multipart/form-data" >
<input type="file" name="upload" /> <br />
<input type="submit" value="Upload" />
</form>
全部瀏覽器都支持上面的代碼。它在 IE 瀏覽器中,顯示以下:
用戶先選擇文件,而後點擊"Upload"按鈕,文件開始上傳。
2、iframe 上傳
傳統的表單上傳,屬於"同步上傳"。也就是說,點擊上傳按鈕後,網頁"鎖死",用戶只能等待上傳結束,而後瀏覽器刷新,跳到表單的 action 屬性指定的網址。
有沒有辦法"異步上傳",在網頁不重載的狀況下,完成整個上傳過程呢?
在 HTML5 沒有出現以前,只能使用 iframe 作到這一點。用戶點擊 submit 時,動態插入一個 iframe 元素(如下代碼使用了 jQuery 函數庫)。
var form = $("#upload-form");
form.on ('submit',function () {
// 此處動態插入 iframe 元素
});
插入 iframe 的代碼以下:
var seed = Math.floor(Math.random() * 1000); var id = "uploader-frame-" + seed; var callback = "uploader-cb-" + seed; var iframe = $('<iframe id="'+id+'" name="'+id+'" style="display:none;">'); var url = form.attr('action'); form.attr('target', id).append(iframe).attr('action', url + '?iframe=' + callback);
最後一行,有兩個地方值得注意。首先,它爲表單添加 target 屬性,指向動態插入的 iframe 窗口,這使得上傳結束後,服務器將結果返回 iframe 窗口,因此當前頁面就不會跳轉了。其次,它在 action 屬性指定的上傳網址的後面,添加了一個參數,使得服務器知道回調函數的名稱。這樣就能將服務器返回的信息,從 iframe 窗口傳到上層頁面。
服務器(upload.php)返回的信息,應該是以下形式:
<script type="text/javascript">
window.top.window['callback'](data);
</script>
而後,在當前網頁定義回調函數:
window[callback] = function (data){
console.log ('received callback:', data);
iframe.remove (); //removing iframe
form.removeAttr ('target');
form.attr ('action', url);
window[callback] = undefined; //removing callback
};
3、ajax 上傳
HTML5 提出了 XMLHttpRequest 對象的第二版,今後 ajax 可以上傳文件了。這是真正的"異步上傳",是未來的主流。上一節的 iframe 上傳,能夠用做老式瀏覽器的替代方案。
ajax 上傳代碼,放在表單的 submit 事件回調函數中:
form.on ('submit',function () {
// 此處進行 ajax 上傳
});
咱們主要用的是 FormData 對象,它可以構建相似表單的鍵值對。
// 檢查是否支持 FormData
if(window.FormData) {
var formData = new FormData ();
// 創建一個 upload 表單項,值爲上傳的文件
formData.append ('upload', document.getElementById('upload').files[0]);
var xhr = new XMLHttpRequest ();
xhr.open ('POST', $(this).attr('action'));
// 定義上傳完成後的回調函數
xhr.onload = function () {
if (xhr.status === 200) {
console.log ('上傳成功');
} else {
console.log ('出錯了');
}
};
xhr.send (formData);
}
4、進度條
XMLHttpRequest 第二版還定義了一個 progress 事件,能夠用來製做進度條。
首先,在頁面中放置一個 HTML 元素 progress。
<progress min="0" max="100" value="0">0</progress>
而後,定義 progress 事件的回調函數。
xhr.upload.onprogress = function (event) {
if (event.lengthComputable) {
var complete = (event.loaded / event.total * 100 0);
var progress = document.getElementById ('uploadprogress');
progress.value = progress.innerHTML = complete;
}
};
注意,progress 事件不是定義在 xhr,而是定義在 xhr.upload,由於這裏須要區分下載和上傳,下載也有一個 progress 事件。
5、圖片預覽
若是上傳的是圖片文件,利用 File API,咱們能夠作一個圖片文件的預覽。這裏主要用到 FileReader 對象。
// 檢查是否支持 FileReader 對象
if (typeof FileReader != 'undefined') {
var acceptedTypes = {
'image/png': true,
'image/jpeg': true,
'image/gif': true
};
if (acceptedTypes[document.getElementById ('upload') .files[0].type] === true) {
var reader = new FileReader ();
reader.onload = function (event) {
var image = new Image ();
image.src = event.target.result;
image.width = 100;
document.body.appendChild (image);
};
reader.readAsDataURL (document.getElementById ('upload') .files[0]);
}
}
6、拖放上傳
最後,利用 HTML5 的拖放功能,實現拖放上傳。
先在頁面中放置一個容器,用來接收拖放的文件。
<div></div>
對它設置樣式:
#holder {
border: 10px dashed #ccc;
width: 300px;
min-height: 300px;
margin: 20px auto;
}
#holder.hover {
border: 10px dashed #0c0;
}
拖放文件的代碼,主要是定義 dragover、dragend 和 drop 這三個事件。
// 檢查瀏覽器是否支持拖放上傳。
if('draggable' in document.createElement ('span')){
var holder = document.getElementById ('holder');
holder.ondragover = function () { this.className = 'hover'; return false; };
holder.ondragend = function () { this.className = ''; return false; };
holder.ondrop = function (event) {
event.preventDefault ();
this.className = '';
var files = event.dataTransfer.files;
// do something with files
};
}
完成後的效果和整體代碼,請看拖放上傳 demo。