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.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、上传客户端
本例中利用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>