Servlet 3.0之前的版本中,文件上传是个挺让人头疼的问题,虽然有第三方框架来实现,但使用也还是比较麻烦,在Servlet 3.0中,这些问题将不复存在,Servlet 3.0对文件上传提供了直接支持,配合Servlet 3.0中基于Annotations的配置,大大简化上传件的操作。今天做了个小demo,测试了写Servlet 3.0的文件上传功能。
1.写文件上传的服务端(Servlet ),将上传文件全都保存到指定配置的目录下面,转向上传文件列表的显示页面。
package com.neusoft.servlet3.demo;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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;
/**
* Servlet implementation class FileUploadServlet
*/
@WebServlet(
description = "文件上传",
urlPatterns = { "/upload" }
)
@MultipartConfig(
location = "E:\\upload",//文件存放路径,指定的目录必须存在,否则会抛异常
maxFileSize = 8388608,//最大上传文件大小,经测试应该是字节为单位
fileSizeThreshold = 819200//当数据量大于该值时,内容将被写入文件。(specification中的解释的大概意思,不知道是不是指Buffer size),大小也是已字节单位
//maxRequestSize = 8*1024*1024*6 //针对该 multipart/form-data 请求的最大数量,默认值为 -1,表示没有限制。以字节为单位。
)
public class FileUploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private String fileNameExtractorRegex = "filename=\".+\"";
/**
* @see HttpServlet#HttpServlet()
*/
public FileUploadServlet() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
throw new UnsupportedOperationException();
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<String> fileNames = new LinkedList<String>();
request.setCharacterEncoding("UTF-8");
Collection<Part> parts = request.getParts();
//遍历所有的表单内容,将表单中的文件写入上传文件目录
for (Iterator<Part> iterator = parts.iterator(); iterator.hasNext();) {
Part part = iterator.next();
//从Part的content-disposition中提取上传文件的文件名
String fileName = getFileName(part);
if(fileName!=null){
fileNames.add(fileName);
part.write(fileName);
}
}
request.setAttribute("fileNames", fileNames);
//显示上传的文件列表
request.getRequestDispatcher("upload_files_list.jsp").forward(request, response);
}
/**
* 从Part的Header信息中提取上传文件的文件名
* @param part
* @return 上传文件的文件名,如果如果没有则返回null
*/
private String getFileName(Part part){
//获取header信息中的content-disposition,如果为文件,则可以从其中提取出文件名
String cotentDesc = part.getHeader("content-disposition");
String fileName = null;
Pattern pattern = Pattern.compile(fileNameExtractorRegex);
Matcher matcher = pattern.matcher(cotentDesc);
if(matcher.find()){
fileName = matcher.group();
fileName = fileName.substring(10, fileName.length()-1);
}
return fileName;
}
}
2.上传文件jsp页面(fileupload.jsp)代码片段,enctype必须申明为multipart/form-data,否则Servlet 会抛异常!
<form action="upload" method="post" enctype="multipart/form-data">
<fieldset>
<label>File1:</label><input type="file" name="file1"/><br/>
<label>File2:</label><input type="file" name="file2"/><br/>
<label>File3:</label><input type="file" name="file3"/><br/>
<label>File4:</label><input type="file" name="file4"/><br/>
<label>File5:</label><input type="file" name="file5"/>
</fieldset>
<input type="submit" name="submit" value="upload"/>
</form>
3.上传文件列表显示页面(upload_files_list.jsp)代码片段。
<p>上传了${fn:length(fileNames) }几个文件 </p>
<c:if test="${not empty fileNames }">
<ul>
<c:forEach var="fileName" items="${fileNames }">
<li>${fileName}</li>
</c:forEach>
</ul>
</c:if>
Servlet 3.0的文件上传功能确实好用,只等Java EE 6普及了,到时候就可以抛弃Commons-FileUpload了!不过Servlet 3.0的文件上传接口也有不足指出,比如不能直接获取上传的文件的文件名,MultipartConfig信息好像也没有提供接口直接获取,我查了好半天没找到,当然没有这些功能也有好处的,那就是带来简单易用接口……当实际有需要的时候再写点扩展的工具类就OK了。
Servlet 3.0还提供了很多其他新功能,最吸引人当然就是对异步处理的支持,有空再研究研究……
PS:Tomcat 7.0正式版已经发布了,今天使用了下,感觉对jsp的处理不怎么好,100%一样的代码我在Glassfish下完美运行,放到tomcat 7就报jsp解析异常(xml解析异常),太不稳定了,看堆栈信息是从xerces中抛出来的,估计跟xerces解析xml有关,懒得深究,还是等以后发布了4、5更新后再考虑使用吧!