第十章_文件上传
10.1、client编程
要上传文件,必须利用multipart/form-data设置HTML表单的enctype的属性值,像以下这样:
<form action="action" enctype="multipart/form-data" method="post"> Select a file<input type="file" name="fieldName" /> <input type="submit" value="Upload" /> </form>
10.2、server端编程
Servlet中的server端文件上传编程主要环绕着MultipartConfig注解类型和javax.servlet.http.Part接口进行。
处理上传文件的Servlet必须用@MultipartConfig进行标注。MultipartConfig能够带有下面属性。这些都是可选的。
maxFileSize,表示最多可上传的文件容量。超过设定值的文件将会遭到拒绝。
默认值为-1,表示不受限制。
maxRequestSize。表示同意多部分HTTP请求的最大容量。
它的默认值为-1。不受限制。
Location。上传的文件保存到磁盘的指定位置,调用Part中的write方法将用到它。
fileSizeThreshold。设定一个溢出尺寸,超过这个值之后。上传的文件将被写入磁盘。
在一个由多部分组成的请求中,每个表单域,包含非文件域,都会被转换成一个Part,HttpServletRequest接口定义了下面方法来处理多部分的请求:
Part getPart(java.lang.String name)
返回与指定名称相关的Part。
java.util.Collection<Part> getParts()
返回这个请求中全部的part
Part接口中还具有下面方法:
java.lang.String getName()
获取到这部分的名称。比如相关表单域的名称
java.lang.String getContentType()
假设Part是一个文件,那么将返回Part的内容类型,否则返回null。
java.util.Collection<java.lang.String> getHeaderNames()
返回这个Part中的全部标头名称
java.lang.String getHeader(java.lang.String headerName)
返回指定标头名称的值
java.util.Collection<java.lang.String> getHeaders(java.lang.String headerName)
返回这个Part中全部标头的名称
Void write(java.lang.String path)
将上传的文件写入磁盘中。假设path是一个绝对路径,那么将写入指定的路径。
假设path是一个相对路径。那么将被写入相对于MultiConfig注解的location属性值的指定路径。
Void delete()
删除该文件相应的存储,包含相关的暂时文件。
java.io.InputStream getInputStream()
以inputStream的形式返回上传文件的内容
假设相关的HTML输入时一个文件input元素,则Part将返回这些标头:
content-type:contentType
content-disposition:form-data; name=”fieldName”; fileName=”fileName”
比如,上传输入域中一个名称document的note.txt文件时,将导致相关的部门也具有这些标头:
content-type:text/plain
content-disposition:form-data; name=”document”; fileName=”note.txt”
假设没有选择不论什么文件,还是会为该文件域创建一个Part。可是相关标头例如以下:
content-type:application/octet-stream
content-disposition:form-data; name=”document”; fileName=””
Part接口的getName返回与这部分有关的域的名称,而不是上传文件的名称。要想返回后者,须要解析content-disposition标头,其格式例如以下:
content-disposition:form-data; name=”fieldName”
在Servlet中处理上传文件时,须要:
1、查看石头存在content-type标头。检验一个Part是属于普通的表单域买还是文件域。你也能够通过在Part中调用getContentType方法或者getHeader(“content-type”)来完毕检查。
2、假设有content-type标头,那么将查看上传文件名称称是否为空。文件名称为空,表示有文件类型的域存在,可是没有选择要上传的文件。
3、假设文件存在。就能够调用Part中的write方法来写入磁盘。调用时同一时候传递一个绝对路径,或是相对于MultipartConfig注解的location属性的路径。
11.3、上传Servlet范例
SingleUploadServlet.java
package app11a.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; @WebServlet(urlPatterns = {"/singleUpload"}) @MultipartConfig public class SingleUploadServlet extends HttpServlet{ private static final long serialVersionUID = 1L; /** * 通过part过得文件名 * @param part * @return */ private String getFileName(Part part){ String contentDispositionHeader = part.getHeader("content-disposition") ; String[] elements = contentDispositionHeader.split(";") ; for(String element: elements){ if(element.trim().startsWith("filename")){ return element.substring(element.indexOf('=') + 1).trim().replace("\"", "") ; } } return null ; } @Override public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8") ; resp.setCharacterEncoding("utf-8") ; Part part = req.getPart("filename") ; String fileName = getFileName(part) ; if(fileName != null && !fileName.isEmpty()){ part.write(getServletContext().getRealPath("/WEB-INF") + "/" + fileName) ; } resp.setContentType("text/html") ; PrintWriter writer = resp.getWriter() ; writer.print("<br/>Upload file name: " + fileName) ; writer.print("<br/>Size: " + part.getSize()) ; String author = req.getParameter("author") ; writer.print("<br/>Author: " + author) ; } }
singleUplpad.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <html> <head> <title>My JSP 'singleUpload.jsp' starting page</title> </head> <body> <h1>Select a file to upload</h1> <form action="singleUpload" method="post" enctype="multipart/form-data"> Author: <input type="text" name="author"/><br/> Select file to upload <input type="file" name="filename"/><br/> <input type="submit" value="上传"/> </form> </body> </html>
11.4、多文件上传
MultipleUploadsServlet.javapackage app11a.servlet; import java.io.IOException; import java.io.PrintWriter; import java.util.Collection; import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; @WebServlet(urlPatterns = {"/multipleUploads"}) @MultipartConfig public class MultipleUploadsServlet extends HttpServlet{ private static final long serialVersionUID = 1L; private String getFileName(Part part){ String contentDispositionHeader = part.getHeader("content-disposition") ; String[] elements = contentDispositionHeader.split(";") ; for(String element: elements){ if(element.trim().startsWith("filename")){ return element.substring(element.indexOf('=') + 1).trim().replace("\"", "") ; } } return null ; } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ request.setCharacterEncoding("utf-8") ; response.setCharacterEncoding("utf-8") ; response.setContentType("text/html") ; PrintWriter writer = response.getWriter() ; Collection<Part> parts = request.getParts() ; for(Part part : parts){ //通过getContentType获得是否是文件域 if(part.getContentType() != null){ String fileName = getFileName(part) ; //通过文件名称获得是否有文件 if(fileName != null && ! fileName.isEmpty()){ part.write(getServletContext().getRealPath("/WEB-INF") + "/" + fileName) ; writer.print("<br/>Uploaded file name: " + fileName) ; writer.print("<br/>Size: " + part.getSize()) ; } }else{ String partName = part.getName() ; String fieldValue = request.getParameter(partName) ; writer.print("<br/>" + partName + ": " + fieldValue) ; } } } }
multipleUploads.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <html> <head> <title>My JSP 'multipleUploads.jsp' starting page</title> </head> <body> <h1>Select a file to upload</h1> <form action="multipleUploads" enctype="multipart/form-data" method="post"> Author: <input name="author"/><br/> First file to upload <input type="file" name="uploadFile"/> <br/> Second file to upload <input type="file" name="uploadFile2"/> <br/> <input type="submit" value="上传"/> </form> </body> </html>
执行截图:
11.5、上传client
本例中利用javascript和HTML5 File API来提供进度条。以报告上传的进度。
html5.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <html> <head> <title>My JSP 'html5.jsp' starting page</title> <script type="text/javascript"> var totalFileLength, totalUploaded, fileCount, filesUploaded ; window.onload = function(){ document.getElementById("files").addEventListener('change', onFileSelect, false) ; document.getElementById("uploadButton").addEventListener('click', startUpload, false) ; } function onFileSelect(e){ var files = e.target.files ;//FileList object var output = [] ; fileCount = files.length ; totalFileLength = 0 ; for( var i=0; i<fileCount; i++){ var file = files[i] ; output.push(file.name, ' (', file.size, ' bytes, ', file.lastModifiedDate.toLocaleDateString(), ')'); output.push("<br/>") ; debug('add ' + file.size) ; totalFileLength += file.size ; } document.getElementById("selectedFiles").innerHTML = output.join('') ; debug('totalFileLength:' + totalFileLength) ; } function debug(s){ var debug = document.getElementById("debug") ; if(debug){ debug.innerHTML = debug.innerHTML + '<br/>' + s ; } } function startUpload(){ totalUploaded = filesUploaded = 0 ; uploadNext() ; } function uploadNext(){ var xhr = new XMLHttpRequest() ; var fd = new FormData() ; var file = document.getElementById('files').files[filesUploaded] ; fd.append("fileToUpload", file) ; xhr.upload.addEventListener("progress", onUploadProgress, false) ; xhr.addEventListener("load", onUploadComplete, false) ; xhr.addEventListener("error", onUploadFailed, false) ; xhr.open("POST", "multipleUploads") ; debug("uploading " + file.name) ; xhr.send(fd) ; } function onUploadProgress(e){ if(e.lengthComputable){ var percentComplete = parseInt(e.loaded + totalUploaded) * 100/totalFileLength ; var bar = document.getElementById("bar") ; bar.style.width = percentComplete + "%" ; bar.innerHTML = percentComplete + " %complete" ; }else{ debug("unable to compute") ; } } function onUploadComplete(e){ totalUploaded += document.getElementById("files").files[filesUploaded].size ; filesUploaded ++ ; debug("complete " + filesUploaded + " of " + fileCount) ; debug("totalUploaded: " + totalUploaded) ; if(filesUploaded < fileCount){ uoloadNext() ; }else{ alert("Finished uploading file(s)") ; } } function onUploadFailed(e){ alert("Error uploading file") ; } </script> </head> <body> <h1>Multiple file uploads with progress bar</h1> <div id="progressBar" style="height:20px;border:2px solid green"> <div id="bar" style="height:100%;background:#33dd33;width:0%"> </div> </div> <form id="form1" action="multipleUploads" enctype="multiple/form-data" method="post"> <input type="file" id="files" multiple/> <br/> <output id="selectedFiles"></output> <input id="uploadButton" type="button" value="Upload"/> </form> <div id="debug" style="height:100px; border:2px solid green; overflow:auto"></div> </body> </html>