可以看到,我们用普通的 <form> 标签来包含一个传统的 <input type=”file” … /> 元素。在 <form> 中还有一个 submit 元素。在 <form> 之外有一些 <span> 元素用来表示已读取和总共的数据量。<form> 的 action 属性指向了一个 JavaScript 函数 uploadAndSubmit()。这个函数完成了读取文件并上传的过程。函数代码见清单 2。
清单 2 读取文件并上传的 JavaScript 函数
function uploadAndSubmit() { var form = document.forms["demoForm"]; if (form["file"].files.length > 0) { // 寻找表单域中的 <input type="file" ... /> 标签 var file = form["file"].files[0]; // try sending var reader = new FileReader(); reader.onloadstart = function() { // 这个事件在读取开始时触发 console.log("onloadstart"); document.getElementById("bytesTotal").textContent = file.size; } reader.onprogress = function(p) { // 这个事件在读取进行中定时触发 console.log("onprogress"); document.getElementById("bytesRead").textContent = p.loaded; } reader.onload = function() { // 这个事件在读取成功结束后触发 console.log("load complete"); } reader.onloadend = function() { // 这个事件在读取结束后,无论成功或者失败都会触发 if (reader.error) { console.log(reader.error); } else { document.getElementById("bytesRead").textContent = file.size; // 构造 XMLHttpRequest 对象,发送文件 Binary 数据 var xhr = new XMLHttpRequest(); xhr.open(/* method */ "POST", /* target url */ "upload.jsp?fileName=" + file.name /*, async, default to true */); xhr.overrideMimeType("application/octet-stream"); xhr.sendAsBinary(reader.result); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { if (xhr.status == 200) { console.log("upload complete"); console.log("response: " + xhr.responseText); } } } } } reader.readAsBinaryString(file); } else { alert ("Please choose a file."); } }
在这个函数中,首先我们找到含有 <input type=”file” … /> 元素的 <form>,并找到含有上传文件信息的 <input> 元素。如 <input> 元素中不含有文件,说明用户没有选择任何文件,此时将报错。
清单 3 找到 <input> 元素
var form = document.forms["demoForm"]; if (form["file"].files.length > 0) { var file = form["file"].files[0]; … … } else { alert ("Please choose a file."); }
这里,从 form[“file”].files 返回的对象类型即为提到的 FileList。我们从中拿取第一个元素。之后,我们构建 FileReader 对象:
var reader = new FileReader();
在 onloadstart事件触发时,填充页面上表示读取数据总量的 <span> 元素。参见清单 4
清单 4 onloadstart 事件
reader.onloadstart = function() { console.log("onloadstart"); document.getElementById("bytesTotal").textContent = file.size; } 在 onprogress 事件触发时,更新页面上已读取数据量的 <span> 元素。参见清单 5
清单 5 onprogress 事件
reader.onprogress = function(p) { console.log("onloadstart"); document.getElementById("bytesRead").textContent = p.loaded; }
最后的 onloadend 事件中,如果没有错误,我们将读取文件内容,并通过 XMLHttpRequest 的方式上传。
清单 6 onloadend 事件
reader.onloadend = function() { if (reader.error) { console.log(reader.error); } else { // 构造 XMLHttpRequest 对象,发送文件 Binary 数据 var xhr = new XMLHttpRequest(); xhr.open(/* method */ "POST", /* target url */ "upload.jsp?fileName=" + file.name /*, async, default to true */); xhr.overrideMimeType("application/octet-stream"); xhr.sendAsBinary(reader.result); … … } }
按照 File API 的规范,我们也可以将事件 onloadend 的处理拆分为事件 error 以及事件 load 的处理。
在这个示例中,我们后台使用一个 JSP 来处理上传。JSP 代码如清单 7。
清单 7 处理上传的 JSP 代码
<%@ page import="java.io.*" %><% BufferedInputStream fileIn = new BufferedInputStream(request.getInputStream()); String fn = request.getParameter("fileName"); byte[] buf = new byte[1024]; //接收文件上传并保存到 d:\ File file = new File("d:/" + fn); BufferedOutputStream fileOut = new BufferedOutputStream(new FileOutputStream(file)); while (true) { // 读取数据 int bytesIn = fileIn.read(buf, 0, 1024); System.out.println(bytesIn); if (bytesIn == -1) { break; } else { fileOut.write(buf, 0, bytesIn); } } fileOut.flush(); fileOut.close(); out.print(file.getAbsolutePath()); %>