這年頭,網頁如果不支援從桌面或檔案總管直接拖拉檔案,想自稱HTML5都心虛,只能稱作HTML4.5(誤),老闆客戶還會不時打你臉: "Gmail、OneDrive(SkyDrive)、DropBox幾百年前就有了! 為什麼你到現在還做不出來?" (網頁攻城獅內心的OS: 你有給我Google或Microsoft等級的薪水嗎?) 搞網頁好辛苦,客戶老闆上網胡亂逛,不小心看到酷炫網站,馬上把規格放進專案,還說什麼瀏覽器打開HTML、CSS、JavaScript都能看,快點抄一抄給我做出來... 想到這我都鼻酸了,Web Developer們應該團結起來,技術衝那麼快是要逼死誰,我數一二三,大家一起放手吧... orz
"大家不要這麼辛苦,好不好?" "好,大家都不要這麼辛苦。" "一、二、三!"
話說回來,既然走了技術這行就得認命,還是乖乖學習怎麼實現檔案拖拉吧!
先看執行效果。目標如下,網頁左上角有個圖檔拖放區,由檔案總管選取多個檔案,拖拉至拖放區放下,網頁接收選取的檔案清單,透過HTML5 File API讀取圖檔轉為Data URI作為<img> src,在頁面呈現圖示清單,點選圖示時可檢視原圖。
【原理解析】
只簡單說明原理,細節請直接看程式碼及註解:
- 拖放操作: 在圖檔拖放區元素加掛ondragover、ondragleave、ondrop事件,滑鼠移拉物件進入離開時切換底色,放下物件時透過transferData.files取得檔案資訊。為避免拖拉操作觸發原有瀏覽器行為,記得要呼叫event.stopPropagation()及event.preventDefault()。
- 檔案資訊: event.transferData.files裡的檔案資訊有name, type, size等資訊,基於安全不包含檔案路徑等資訊,但可當成File API參數讀出內容。
- 讀取檔案: 透過HTML5 File API FileReader(),將圖檔讀入轉成Data URI,可直接做為<img> src顯示圖檔。【延伸閱讀: 】
- 清單及檢視: 採用Knockout MVVM實作清單、檢視介面。
程式範例如下: Live Demo
<!DOCTYPEhtml>
<html>
<head>
<metaname="viewport"content="width=device-width"/>
<title>HTML5 拖拉圖檔顯示在網頁上</title>
<style>
.drop-zone {
position: absolute; top: 6px; width: 120px; height: 90px;
background-color: green; color: white; text-align: center;
}
.drop-zone.hover {
background-color: blue;
}
.img-list {
position: absolute; height: 90px; background-color: #444;
top: 6px; left: 135px; right: 6px; overflow-y: hidden;
overflow-x: auto; white-space: nowrap;
}
.thumbnail {
max-width: 100px; max-width: 75px; vertical-align: top;
margin: 3px; cursor: pointer;
border: 1px solid transparent;
}
.thumbnail:hover {
border: 1px solid red;
}
.display {
position: absolute; top: 110px;
left: 6px; right: 6px; bottom: 6px;
padding: 12px;
}
</style>
</head>
<body>
<divclass="drop-zone"><span>圖檔拖放區</span></div>
<divdata-bind="foreach: images"class="img-list">
<imgdata-bind="attr: { src: dataUri }, click: $root.currImage"class="thumbnail"/>
</div>
<fieldsetclass="display"data-bind="with: currImage">
<legend>
<spandata-bind="text: name"></span>
<spandata-bind="text: size"></span>
</legend>
<div>
<imgdata-bind="attr: { src: dataUri }"/>
</div>
</fieldset>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.js "></script>
<script src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-3.0.0.js "></script>
<script src="http://cdn.kendostatic.com/2013.3.1119/js/kendo.core.min.js"></script>
<script>
$(function () {
var $drop = $(".drop-zone");
//抑制瀏覽器原有的拖拉操作效果
function stopEvent(evt) {
evt.stopPropagation();
evt.preventDefault();
}
$drop.bind("dragover", function (e) {
//滑鼠經過上方時加入特效
stopEvent(e);
$(e.target).addClass("hover");
}).bind("dragleave", function (e) {
//滑鼠移開時移除特效
stopEvent(e);
$(e.target).removeClass("hover");
}).bind("drop", function (e) {
//拖放操作完成事件
stopEvent(e);
$(e.target).removeClass("hover");
//由dataTransfer.files取得檔案資訊
var files = e.originalEvent.dataTransfer.files;
var imageFiles = $.map(files, function (f, i) {
//只留下type為image/*者,例如: image/gif, image/jpeg, image/png...
return f.type.indexOf("image") == 0 ? f : null;
});
//清除ViewModel
vm.images.removeAll(); vm.currImage(null);
//逐一讀入各圖檔,取得DataURI,顯示在網頁上
$.each(imageFiles, function (i, file) {
//使用File API讀取圖檔內容轉為DataUri
var reader = new FileReader();
reader.onload = function (e) {
//將檔名、檔案大小、DataURI放入ViewModel
vm.images.push({
name: file.name,
size: kendo.format("{0:n0} bytes", file.size),
dataUri: e.target.result
})
}
reader.readAsDataURL(file);
});
});
function myViewModel() {
var self = this;
self.images = ko.observableArray();
self.currImage = ko.observable();
}
var vm = new myViewModel();
ko.applyBindings(vm);
});
</script>
</body>
</html>
【參考資料】