10.1、客户端编程
要上传文件,必须利用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、服务器端编程
Servlet中的服务器端文件上传编程主要围绕着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.java
- package 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、上传客户端
本例中利用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>