HTML 5应用实战:灵活拖拉文件
这篇文章将分如下几个方面进行初步的分析和探讨:
◆ 如何将文件拖放到web页面中
◆ 在Javascript中分析拖拽文件
◆ 在客户端装载和解析文件
◆ 使用 XMLHttpRequest2异步上传文件到服务端
◆ 上传时显示一个进度条
◆ 改进上传文件的表单,甚至支持IE 6,以让在各浏览器中可以兼容运行。
◆ 并介绍如何单单只用Javascript实现,不用任何框架。
目前浏览器对文件拖拉的支持
在我们开始讲解前,我们先要说明下,由于HTML5的标准最终版本还没完全发布,各浏览器也不是完全都对所有功能进行支持,所以本文的程序有可能在今后的各浏览器中不能完全运行成功,但至少在本文发表时,会对以下浏览器在如下各方面进行支持。
1) 目前程序能在所有的Firefox和Chrome的浏览器中很好的运行。
2) Opera浏览器能解通过Javascript去解析文件,但不支持文件的拖拉到浏览器中及使用XMLHttpRequest2去上传文件。
3) IE和Safari不支持任何本文提到的API和新特性。
4) Apple不允许在iOS系统中使用HTML表单上传文件。
下面,就让我们开始学习之旅吧。
HTML和CSS
我们先来看下上传文件的表单,代码如下:
<form id="upload" action="upload.php" method="POST" enctype="multipart/form-data"> <fieldset> <legend>HTML File Upload</legend> <input type="hidden" id="MAX_FILE_SIZE" name="MAX_FILE_SIZE" value="300000" /> <div> <label for="fileselect">Files to upload:</label> <input type="file" id="fileselect" name="fileselect[]" multiple="multiple" /> <div id="filedrag">or drop files here</div> </div> <div id="submitbutton"> <button type="submit">Upload Files</button> </div> </fieldset> </form> <div id="messages"> <p>Status Messages</p> </div>
可以看到,在这个表单中,放置了一个文件选择框,唯一跟HTML4不同的时,这个框采用的是HTML5中的一个新增的文件上传框属性multiple,允许选择多个不同的文件。此外,在id为 filedrag的div中,这个区域是用来将文件拖拉放置的区域,下文中会用到。
接下来会看下CSS,代码如下:
#filedrag { display: none; font-weight: bold; text-align: center; padding: 1em 0; margin: 1em 0; color: #555; border: 2px dashed #555; border-radius: 7px; cursor: default; } #filedrag.hover { color: #f00; border-color: #f00; border-style: solid; box-shadow: inset 0 3px 4px #888; }
在这段CSS中,设置了当文件拖放到页面的指定区域时的样式,这个样式是当文件被拖动到指定区域时,才起到作用的,是通过Javascript击发其起作用的,下文会提到。
HTML5中的文件API
在HTML5中,对文件的API有如下几点的新的改进,其标准可以参考(http://www.w3.org/TR/file-upload/)。
FileList: 表示已选择的文件,以数组的形式表示。
File: 代表一个单独的文件
FileReader: FileReader是一个接口,它允许我们在客户端读取文件的数据,并且可以在Javascript中去使用。
开始动手写Javascript
下面,我们开始动手编写Javascript,首先我们编写一些工具javascript,比如:
// getElementById function $id(id) { return document.getElementById(id); } // // 输出信息 function Output(msg) { var m = $id("messages"); m.innerHTML = msg + m.innerHTML; }
接者,我们编写init方法,去判断文件API的可用性,代码如下:
// 判断当前浏览器中文件API是否可用 if (window.File && window.FileList && window.FileReader) { Init(); } // // 初始化程序 function Init() { var fileselect = $id("fileselect"), filedrag = $id("filedrag"), submitbutton = $id("submitbutton"); // 添加文件选择的事件监听 fileselect.addEventListener("change", FileSelectHandler, false); // 判断xmlhttprequest 2是否可用 var xhr = new XMLHttpRequest(); if (xhr.upload) { // file drop filedrag.addEventListener("dragover", FileDragHover, false); filedrag.addEventListener("dragleave", FileDragHover, false); filedrag.addEventListener("drop", FileSelectHandler, false); filedrag.style.display = "block"; // remove submit button submitbutton.style.display = "none"; } }
程序中,首先判断当前浏览器中文件API是否可用,如果可用的话则调用init的初始方法。在init方法中,做了如下几件事情:
为文件的选择框增加了上传的监听事件。
显示#filedrag区域
设置了拖动文件经过放置区域时和文件离开放置区域时的事件以及样式。
设置了当用户将文件拖动到放置区域最后松开鼠标,以决定文件放置时的事件。
这里还隐藏了提交按钮,因为这里并不需要它了。
当然,这里还可以连传统的文件上传浏览框也去掉,但这并不是十分友好,所以这里采用的是两者并存的方式。
而用到了XMLHttpRequest的upload方法,主要是防止在一些浏览器中如果当文件上传API不可用时,则用旧的方法,不显示文件拖拉框,取而代之的是显示传统的上传提交按钮。
考虑到很少用户很熟悉文件的这种拖拉上传用法,因此,我们在事件中对文件放置区域的样式进行设置,当文件被拖拉到区域中时,区域的边框会变成红色,以提醒用户,代码如下:
function FileDragHover(e) { e.stopPropagation(); e.preventDefault(); e.target.className = (e.type == "dragover" ? "hover" : ""); }
这里判断是否文件拖拉到区域上方,如果是的话,则采用样式中的hover样式。
接下来,我们会在另外的一个div区域中,显示文件上传的小结信息,代码如下:
function FileSelectHandler(e) { FileDragHover(e); // 获得所有的文件列表 var files = e.target.files || e.dataTransfer.files; // 循环处理每个文件 for (var i = 0, f; f = files[i]; i++) { ParseFile(f); } }
在FileSelectHandler事件中,实现了如下的功能:
1 调用FileDragHover()方法移除了hover的样式,并且取消浏览器的响应事件,否则的话浏览器会尝试显示文件的内容。
2 将上传的文件放置到FileList对象数组中去,包括用传统文件上传框上传的及用拖拉方法上传的。
3 单独使用一个方法ParseFile输出上传每个文件的具体属性信息,代码如下;
function ParseFile(file) { Output( " File information: " + file.name + " type: " + file.type + " size: " + file.size + " bytes " ); }
使用Javascript打开客户端文件
现在,我们已经把客户端机器上的文件成功拖拉到浏览器中了,也就是说,我们用新的拖拉的方法,完成了以往要使用传统的文件上传选择框才能完成的工作!接下来,我们尝试通过Javascript,去判断用户上传的文件类型,并学习如何使用Javascript去操作客户端的文件。
首先,为什么要通过Javascript去分析操作客户端的文件呢?假如要求用户上传很多文件,或者上传指定格式的图片,假如用户选好上传文件到服务端后,服务端才判断发现文件不符合要求,再要求用户重新上传的话,给用户的体验则不大友好,所以,希望能在用户在客户端选择完文件后就马上能判断出文件的大小和类型,如果是图片的话,甚至可以实现客户端图片预览的效果。
在HTML5的起草标准中,提供了FileReader接口去处理客户端的文件,有如下几个重要API:
readAsText(File f, [encoding]):将一个文件读入到字符串中去,其中可以选用文件的编码,默认的是UTF-8
.readAsDataURL(File f): 将文件以data URL编码的方式读入文件内容,dat Url方式是一种以BASE64编码在页面中展示文件的方法,详细见:http://en.wikipedia.org/wiki/Data_URI_scheme
.readAsBinaryString(File f): 将文件以二进制方式读取。.readAsArrayBuffer(File f):.将文件作为ArrayBuffer对象的方式读取。ArrayBuffer详见http://www.khronos.org/registry/typedarray/specs/latest/
接下来,我们看下如何在Javascript中异步打开读取文件。
Javascript中异步打开文件
先来复习下之前我们用到的parseFile方法,代码如下:
function ParseFile(file) { Output( " File information: " + file.name + " type: " + file.type + " size: " + file.size + " bytes " ); }
这里只是简单显示了文件的文件名和文件大小,而接下来我们要判断是这个文件是否属于文件类型的文件(即text/plain,text/html,text/css等),可以使用FileReader.readAsText()方法读取并且去掉其中的<,>符号,代码如下;
if (file.type.indexOf("text") == 0) { var reader = new FileReader(); reader.onload = function(e) { Output( " " + file.name + ": " + e.target.result.replace(//g, ">") + " " ); } reader.readAsText(file); }
同样,我们希望将一张图片从用户的资源管理器中,拖拉到页面的放置区域后,能马上显示这张图片的实际内容以及大小,可以用如下代码实现:
0.// display an image 0.if (file.type.indexOf("image") == 0) { 0. var reader = new FileReader(); 0. reader.onload = function(e) { 0. Output( 0. "<p><strong>" + file.name + ":</strong><br />" + 0. '<img src="' + e.target.result + '" /></p>' 0. ); 0. } 0. reader.readAsDataURL(file); 0.}
这里,直接将图片的内容以FileReader的readAsDataURL方法读取入并显示。
最后,我们可以通过http://blogs.sitepointstatic.com/examples/tech/filedrag/2/index.html观看我们的
DEMO演示,请读者尝试从自己电脑上的windows资源管理器中,拖放图片或其他类型文
件到页面的指定区域中,则会发现浏览器能自动识别用户拖放的文件,当然,请使用Chrome
或Firefox浏览器才能看到效果。
小结