方法:
- 使用 HTML5 新增的拖曳功能(Drag and Drop),將電腦檔案拖曳到網頁上。
- 拖曳時會產生幾種事件,這些事件會產生 DragEvent 物件。
- DragEvent 物件有一個 dataTransfer 屬性,由dataTransfer 屬性,可取得 DataTransfer 物件。
- 由 DataTransfer 物件的 files 屬性,即可取得檔案物件(FileList)。
- 將取得的檔案物件丟給 FormData 物件。
FormData:http://www.w3.org/TR/XMLHttpRequest2/#interface-formdata - 最後將 FormData 的資料,丟給 XMLHttpRequest 使用 AJAX 方式上傳到網站伺服器。
以下範例可拖曳多個 JPG 圖檔,一次上傳,並顯示上傳進度。
(於Firefox、Chrome、IE10 測試可正常運作)
HTML 與 JavaScript 的部份如下
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>HTML5拖曳多檔案上傳</title> <style> #dropDIV{ text-align: center; width: 300px; height: 200px; margin: auto; border: dashed 2px gray; } img{ max-height:200px; max-width:300px; } </style> <script> function dragoverHandler(evt) { evt.preventDefault(); } function dropHandler(evt) {//evt 為 DragEvent 物件 evt.preventDefault(); var files = evt.dataTransfer.files;//由DataTransfer物件的files屬性取得檔案物件 var fd = new FormData(); var xhr = new XMLHttpRequest(); var up_progress = document.getElementById('up_progress'); xhr.open('POST', 'upload.php');//上傳到upload.php xhr.onload = function() { //上傳完成 up_progress.innerHTML = '100 %, 上傳完成'; }; xhr.upload.onprogress = function (evt) { //上傳進度 if (evt.lengthComputable) { var complete = (evt.loaded / evt.total * 100 | 0); if(100==complete){ complete=99.9; } up_progress.innerHTML = complete + ' %'; } } for (var i in files) { if (files[i].type == 'image/jpeg') { //將圖片在頁面預覽 var fr = new FileReader(); fr.onload = openfile; fr.readAsDataURL(files[i]); //新增上傳檔案,上傳後名稱為 ff 的陣列 fd.append('ff[]', files[i]); } } xhr.send(fd);//開始上傳 } function openfile(evt) { var img = evt.target.result; var imgx = document.createElement('img'); imgx.style.margin = "10px"; imgx.src = img; document.getElementById('imgDIV').appendChild(imgx); } </script> </head> <body> <div id="dropDIV" ondragover="dragoverHandler(event)" ondrop="dropHandler(event)"> 拖曳圖片到此處上傳 <div id="up_progress"></div> </div> <div id="imgDIV"></div> </body> </html>伺服器端處理上傳圖檔的 PHP 程式(upload.php)
$uploads_dir = 'mydir';//存放上傳檔案資料夾 foreach ($_FILES["ff"]["error"] as $key => $error) { if ($error == UPLOAD_ERR_OK) { $tmp_name = $_FILES["ff"]["tmp_name"][$key]; $name = $_FILES["ff"]["name"][$key]; move_uploaded_file($tmp_name, "$uploads_dir/$name"); } }
DEMO:
- 本文章的範例
http://demo.cinc.biz/html5-drag-drop-upload/ - 加了顯示上傳後圖片的範例
http://demo.cinc.biz/html5-drag-drop-upload/index2.html - 承上,另外用client端設定儲存的檔名
http://demo.cinc.biz/html5-drag-drop-upload/index3.html - 先預覽不自動上傳,可刪除預覽圖片,最後再一起上傳
http://demo.cinc.biz/html5-drag-drop-upload/preview-del.html
- 「var complete = (evt.loaded / evt.total * 100 | 0);」,最後的「 | 0」使用到 bitwise OR 運算,是用來捨去小數的部份。可參考
http://stackoverflow.com/questions/7487977/using-bitwise-or-0-to-floor-a-number
http://jsperf.com/or-vs-floor/2 - 若想使用 jQuery 上傳 FormData 的資料可參考
http://stackoverflow.com/questions/6974684/how-to-send-formdata-objects-with-ajax-requests-in-jquery
http://stackoverflow.com/questions/5392344/sending-multipart-formdata-with-jquery-ajax
不能用阿...
回覆刪除伺服器收不到檔案
您好,是指上傳進度有到 100%,但伺服器端沒看到檔案嗎?
刪除如果是,請檢查伺服器的回應訊息,看是否有發生錯誤。
上面的範例沒有將回應訊息顯示出來,
可以用各瀏覽器的開發者工具,查看 AJAX 的回應訊息。
或是在上面的 JS 範例,加上顯示回應訊息的程式碼,例如
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
alert("伺服器回應:\n" + xhr.responseText);
}
}
也可以先用傳統的上傳方式,測看看伺服器端是否正常
<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="ff[]" />
<input type="submit" value="上傳" />
</form>
還有伺服器端的 mydir 資料夾要自己建立
form 我測試過了可以上傳 但拖曳就是不能上傳
刪除不知哪裡出了問題
您好,可以用瀏覽器的開發人員工具,看裡面的
刪除1.主控台(console)是否有 JS 的錯誤訊息
2.網路(network)是否有產生上傳的request
或是將您的測試程式寄給我看看
xyz@cinc.biz
另外請問您使用的瀏覽器與版本是?
補充一下,叫出瀏覽器內建的 開發人員工具 快速鍵如下
刪除Firefox:「shift + F2」
chrome:「ctrl + shift + i」
IE:「F12」
非常感謝 回應 我webserver 是架設在 fedora 20 apache2 開發人員工具 也看不出問題 測試程式跟網頁的一樣只改 body 內div的排列 您說的網路是否有產生上傳的request 要如何看
刪除利用開發人員工具 確認 upload.php 有被啟動 另外圖片有14MB左右 然後就無法上傳 php.ini post_max_size upload_max_filesize 我都加大到 32MB 了 目前找不到問題下手解決
刪除非常感謝我已經找到問題 是 selinux 的問題 目前還未找到設定解決 但知道是確認是selinux設定問題 感謝您寶貴的範例
刪除晚了一步 XD,我剛弄了個demo
刪除http://demo.cinc.biz/html5-drag-drop-upload/
demo就留著囉,給之後有緣的人看 XD
假設上傳檔名為 abc.jpg
上傳後,圖片網址為
http://demo.cinc.biz/html5-drag-drop-upload/mydir/abc.jpg
可以查看是否上傳成功
如果是 selinux 的問題
似乎可以在 upload.php 回應訊息看到執行 move_uploaded_file() 時,產生PHP Warning 的訊息
假設PHP 錯誤訊息,顯示的層級,有開到很高的話,開發時我是都開到最高,notice 都顯示 XD
解決的方式可以參考
http://albertech.net/2011/03/fix-fedora-selinux-permissions-for-php-file-upload/
感謝分享!! 我剛好也看到這個網站 分享一下可能版本與系統環境的關係還有幾個項目需要測試
刪除sudo chcon -R -t httpd_sys_rw_content_t 目錄
sudo setsebool -P httpd_usified 1
sudo setsebool -P httpd_read_user_content 1
分享可能線索
感謝分享,剛好我對 SELinux 的設定也不熟 XD
刪除您好
回覆刪除我在第59行發生error
POST http://localhost/upload_test/upload.php 405 (Method Not Allowed)
請問要怎麼解決QQ
您好
刪除看起來應該是網頁伺服器設定成不能接收 POST 的資料
可能要修改網頁伺服器的設定。
IIS 可以參考:jQuery POST, Error 405 Method not allowed
Nginx 可以參考:Nginx HTTP Post Method: 405 Method not allowed解决方法
最後決定改用asp寫了
刪除謝謝您
您好,可以請問您原本是用什麼寫呢?
刪除我自己是用 Apache+PHP,
之前測試限制不能使用 POST ,出現的訊息不是 405,
所以還蠻好奇的您是不是用 Apache,謝謝~
您好,我是用IIS沒有用apache耶
刪除不好意思沒有幫上你的忙
您的文章真的很棒 很有幫助
謝謝您
您好,有幫到我喔,因為我之前在猜您是不是用 IIS,但不確定。
刪除我剛剛試了一下 IIS 7.5 + PHP(用FastCgiModule)
將"處理常式對應"->"編輯模組對應"->"要求限制"->"指令動詞" 改成只接受 GET
真的出現 "POST upload.php 405 Method Not Allowed" 了
解決我心中的疑惑,感謝~
您好:想請教一下~
回覆刪除該怎樣實現此範例拖曳圖片後..能夠在我的html 指定 div 裏面出現呢??
我對於後端怎樣串資料給前端沒有研究...
但我在想..html5 是不是會有更人性化的功能出現?
讓管理者在指定上傳的區域放上圖片後.點選傳送 .,可以在前台的首頁看到變更.....
有想過用xml去串...但是還不夠人性化@@....
本來想放棄的..但看到你的教學.感覺還是有希望
您好
刪除我修改原範例,加了顯示上傳後的圖片,供您參考
http://demo.cinc.biz/html5-drag-drop-upload/index2.html
主要修改是在 PHP 傳回了上傳後的檔名,JavaScript 增加了 xhr.onreadystatechange 處理 PHP 回傳的檔名資料
關於"後台上傳後,前台可以看到變更"
我習慣將上傳後的檔案名稱,儲存在資料庫,前台再去撈資料庫儲存的檔案名稱。
另外,您提到使用 HTML5 的功能達到此效果,
我在猜,您是想做正式儲存前的預覽效果嗎?
如果在同一個瀏覽器,我目前是想到,可以使用HTML5的 postMessage 的功能,
假設一開始,已從後台開啟前台,此時瀏覽器有兩個頁面,
這時後台可以用 postMessage,傳遞資料給被開啟的前台
就可以讓前台更換圖片,達到預覽的效果。
你好!請問要上傳照片去 網上directory 可以怎樣做?
回覆刪除我嘗試把$uploads_dir = 改做想存放的位置-->>'http://maki1824.byethost11.com/Wedding/photos/'
但是行不通,,請指教
您好,
刪除假設範例中的 upload.php 放在 Wedding 資料夾底下
則 $uploads_dir = 'photos';
photos 資料夾要先建立好,並確認有寫入的權限。
謝謝已解決!
刪除然後想問一下若要限制上傳圖片的大小, 可以怎樣做?
server 端可以用 $_FILES['ff']['size'] 取得上傳檔案大小
刪除如果是多個檔案,可如範例中,跑迴圈處理。
PHP上傳後的檔案陣列 $_FILES 可用的資訊,可參考
http://www.php.net/manual/en/features.file-upload.post-method.php
client 端可以在 for (var i in files) {...} 這個地方,
使用 files[i].size 取得檔案大小,再進行判斷。
可參考 https://developer.mozilla.org/en-US/docs/Web/API/Blob.size
只在 server 端限制,有一個缺點=>要上傳完才知道有無超過大小。
只在 client 端限制,缺點則是=>可能會被繞過限制。
請問要如何可以在上傳前改變上傳檔名,因為不想在server端php改,client端給隨機碼讓server有覆蓋同一批圖片的可能
回覆刪除files[i].name="????";
alert(files[i].name)
出來後發現檔名沒有被我改變,還是原始檔名
新手求解 plz
您好
刪除因為 File.name 屬性是唯獨的,所以沒辦法修改
https://developer.mozilla.org/en-US/docs/Web/API/File
一般我都是在server端產生唯一的檔名。
如果您想在client端設定上傳後的檔名,
可以用一個隱藏欄位來存放設定的檔名,再一併送到server端
server端儲存檔案時,再用接收到的檔名來儲存檔案
以文中範例來說,server端PHP大概類似這樣
move_uploaded_file($tmp_name, "$uploads_dir/接收到的檔名")
我剛剛弄了個範例,您參考看看
刪除http://demo.cinc.biz/html5-drag-drop-upload/index3.html
不過我直接在 JS 裡面設定要一起傳送的檔名資料,加了
fd.append('fn[]', 'newfilename' + i);//設定上傳後的檔案名稱,上傳後名稱為 fn 的陣列
設定的檔名依序為 newfilename0、newfilename1、newfilename2、.....
感謝了!!!原來是唯讀屬性,只好乖乖帶值傳過去了
刪除您好:
回覆刪除感謝您的提供的範例 但是我有碰到問題想要請教一下
我要從前面頁面的form傳遞一個參數來放置我要的路徑
可是檔案卻無法放置在我所要放置的路徑上 這個是哪方面的問題呢?
$error) {
if ($error == UPLOAD_ERR_OK) {
$tmp_name = $_FILES["ff"]["tmp_name"][$key];
$name = $_FILES["ff"]["name"][$key];
move_uploaded_file($tmp_name, $uploads_dir.$name);
}
}
?>
$v_id = $_POST['f_id'];
刪除$uploads_dir = "/home/Taian/paper/".$v_id."/";
mkdir($uploads_dir, 0755, true);
foreach ($_FILES['ff']["error"] as $key => $error) {
if ($error == UPLOAD_ERR_OK) {
$tmp_name = $_FILES["ff"]["tmp_name"][$key];
$name = $_FILES["ff"]["name"][$key];
move_uploaded_file($tmp_name, $uploads_dir.$name);
}
}
?>
您好,是指檔案上傳成功,但路徑不是您設的嗎?
刪除如果是,$_POST['f_id'] 接收到值是?
另外,因您沒貼 JS 的部份,請問 JS 有使用 fd.append 將 f_id 增加到要傳送的資料裡嗎?
您好
刪除JS 我只有修改上傳的upload.php 而已 其他的還不清楚要如何調整
我想要透過丟過去upload.php的參數來指定我要放置檔案的路徑
這部分不清楚該如何修改
所以是我有少增加語法嗎?
感謝您的回覆
我前端只是希望透過一個 input name='f_id' type='text' value="A1234567890" 傳遞
刪除使其將檔案放置在 A1234567890 這個路徑下
這些我寫在一個< form > 裡面
< form enctype='multipart/form-data' action="test.php" method="post" name="form_member_login" class="form_block" >
< p class="form_text" >
< input name="but_paper_upload" type="submit" value="登入" / >
< /p >
< p >
< div id="dropDIV" ondragover="dragoverHandler(event)" ondrop="dropHandler(event)" >
拖曳圖片到此處上傳
< div id="up_progress" >< /div >
< /div >
< div id="imgDIV" >< /div >
< /p >
1.HTML 部份
刪除<form id="myForm" ....>
<input name='f_id' type='text' value="A1234567890">
...略...
</form>
2.JS 部份,FormData 綁定 HTML 的 form,POST 時會連表單資料一起傳送
var fd = new FormData();
改成
var fd = new FormData(document.getElementById("myForm"));
這樣PHP應該就可以接收到 $_POST["f_id"] 的值了。
P.S.若不想綁定整個 form,也可以使用 fd.append('f_id', 'A1234567890') 將要傳送的資料個別加進去
請問如果我要加入按鈕 也是透過JAVASCRIPT嗎
回覆刪除預覽完圖片後 在點擊按鈕上傳
刪除按鈕直接放在HTML即可
刪除簡易的修改如下
1. JavaScript 中 fd、xhr 改成全域變數
var fd = new FormData();
var xhr = new XMLHttpRequest();
改成
fd = new FormData();
xhr = new XMLHttpRequest();
2.拿掉 JavaScript 開始上傳的程式碼
xhr.send(fd);//開始上傳 => 這行拿掉
3.HTML按鈕的onclick加上開始上傳的程式碼
<input type="button" value="開始上傳" onclick="xhr.send(fd);">
請教一下~
回覆刪除如果我要限制拖曳的檔案數量要怎麼做呢?
例如只能接受1個檔案拖曳~
謝謝
您好,可以在「var files = evt.dataTransfer.files;//由DataTransfer物件的files屬性取得檔案物件」這一行後面,使用 files.length 屬性,加上拖曳數量判斷。
刪除例如:
.....
var files = evt.dataTransfer.files;//由DataTransfer物件的files屬性取得檔案物件
if(files.length >1){
alert("一次只能上傳一張圖片");
return;
}
.....
你好,我有問題想請教版主
回覆刪除假設我一次拖曳8張圖片做預覽之後,然後刪除了第3張和第5張
之後在選其他圖片拖曳進去或者不選,確認之後,才將上述圖片一次性上傳
這樣的話,我就沒辦法在一開始拖曳的時候做
var fd = new FormData();
var xhr = new XMLHttpRequest();
fd.append('ff[]', files[i]);
xhr.send(fd);
這些動作,
原本想說自己建 input file再將檔案塞進去 結果發現不行
想請問版主,我應該怎處理...
或者再刪除的時候,可否將ff[]指定的圖檔刪除?
抱歉 我是原PO 想請問一下
刪除我在form裡,進行拖曳圖片或是點選圖檔都可以在瀏覽器預覽(這一段沒問題)
form裡,也包含一些其他input訊息,再透過submit方式,在 publish_act.php 儲存資料
form裡面 我設定 action="publish_act.php" method="post" id="view_form" enctype="multipart/form-data"
接著 我在submit的時候
加上這兩行,再submit
xhr.open('POST', 'publish_act.php');
xhr.send(fd);//開始上傳
但是我在 publish_act.php 這支
print_r($_FILES["ff"]);
卻沒有任何資料!!
想請問 我是哪一段出問題
是ff[]要先設定input嗎?
還是 xhr.open('POST', 'publish_act.php'); 這一段有問題..
我用另一隻js去做拖曳和預覽的動作,內容如下:
===========================================================================================
// 要使用全域變數
fd = new FormData($('view_form')[0]);
xhr = new XMLHttpRequest();
//xhr.open('POST', 'publish_detail_act.php'); //上傳到publish_detail_act
function dragoverHandler(evt)
{
evt.preventDefault();
}
function dropHandler(evt)
{
evt.preventDefault();
var files = evt.dataTransfer.files; //由DataTransfer物件的files屬性取得檔案物件
var pic_count = $('#pic_area').children('li').length;
for(var i=0;i<files.length;i++) {
var pic_file = files[i];
//將圖片在頁面預覽
var fr = new FileReader();
fr.onload = openfile;
fr.readAsDataURL(files[i]);
//新增上傳檔案,上傳後名稱為 ff 的陣列
fd.append('ff[]', files[i]);
}
//xhr.send(fd);//開始上傳
}
function openfile(evt) // 預覽顯示
{
......
}
function getMultiPic(input)
{
var files = input.files;
if(typeof(FileReader) != 'undefined')
{
var pic_count = $('#pic_area').children('li').length;
for(var i=0;i<files.length;i++) {
var pic_file = files[i];
//將圖片在頁面預覽
var fr = new FileReader();
fr.onload = openfile;
fr.readAsDataURL(files[i]);
//新增上傳檔案,上傳後名稱為 ff 的陣列
fd.append('ff[]', files[i]);
}
//xhr.send(fd);//開始上傳
}
}
==========================================================================================
您好,我做了個範例供您參考(目前範例server有問題,無法上傳,請自行參考程式碼在您的環境測試)
刪除http://demo.cinc.biz/html5-drag-drop-upload/preview-del.html
範例是讀取預覽圖片的data URI進行上傳,
另外我想如果記錄、刪除拖曳產生的file物件,應該也可以,但要想如何紀錄、刪除。
謝謝你 我之前以為
刪除xhr.open('POST', 'publish_act.php');
xhr.send(fd);//開始上傳
然後在做 form.submit();
兩邊的資訊 就會一起post到另一個頁面
結果我錯了
原來 xhr.send(fd) 是背景去執行...
我改變做法了
在拖曳或是選檔案時,先做xhr.send(fd) 存到暫存的資料夾
然後本頁也存圖片的檔名
submit之後
再將檔名和暫存的資料夾裡面的檔案做比對
在將圖片移到我要的目錄
糗大了 在手機上 不能拖曳 囧rz...
刪除作者已經移除這則留言。
回覆刪除如何用成所有檔案都能上傳,不只有jpg這格式
回覆刪除您好,JS 的部分是在這裡過濾掉了
刪除if (files[i].type == 'image/jpeg') {
...
}
至於 PHP 的部分,本文中的範例沒有過濾,
但後面其他 DEMO 的程式,部分有過濾,也是修改有出現 jpg 或 jpeg 的地方。
這篇幫到我好多~ 太感謝了!
回覆刪除