我们可以知道在servlet3.0前上传文件是利用fileupload组件来上传,而3.0新增的multipartconfig注解简化了上传文件的复杂度。
一、客户端编程
要上传文件,必须利用multipart/form-data设置HTML表单的enctype属性值:
<form action="servlet/FileServlet" enctype="multipart/form-data" method="post"> <input type="file" name="uploadFile" multiple/> <input type="text" name="tag" /> <input type="submit" value="Upload"/> form>
二、服务器端编程(servlet编写)
1. MultipartConfig注解与http.part接口的使用
(1) MultipartConfig可以带有以下属性,都为可选的:
maxFileSize:表示最多可以上传的文件的容量,超过设定值的文件将会遭到拒绝,默认值为-1,表示无限制。
maxRequestSize:表示允许多部分HTTP请求的最大容量,默认值为-1,意味着它是不受限制的。
location:将上传的文件保存在服务器中项目的指定位置,调用Part的write方法会使用,但是,只有write中填写为相对路径,那么意味着是相对于location的路径,如果是填写为绝对路径,则与location无关。
fileSizeThreshold:设定溢出大小,超过这个值那么上传的文件就会被写入磁盘
(2) http.part接口:
在一个有多部分请求组成的请求中,每一个表单域,包括非文件域,都会被转换成一个个Part。
HttpServletRequest接口定义了以下方法来获取Part:
Part getPart(String name) 返回与指定名称相关的Part,名称为相关表单域的名称(name)
Colleciton
Part接口中定义的方法:
String getName() 获取这部分的名称,例如相关表单域的名称
String getContentType() 如果Part是一个文件,那么将返回Part的内容类型,否则返回null(可以利用这一方法来识别是否为文件域)
Collection
String getHeader(String headerName) 返回指定标头名称的值
void write(String path) 将上传的文件写入服务器中项目的指定地址下,如果path是一个绝对路径,那么将写入指定的路径,如果path是一个相对路径,那么将被写入相对于location属性值的指定路径。
InputStream getInputStream() 以inputstream的形式返回上传文件的内容
2. 说明
如果HTML的输入是一个文件域,那么Part将返回这些标头:
content-type:contentType
content-disposition:form-data; name="filedName"; filename="fileName"
如果是一个普通文本域,那么:
content-disposition:form-data; name="filedName"
如果是文件域,但没有上传文件,那么:
content-type:application/octet-stream
content-disposition:form-data; name="filedName"; filename=""
3. 实例
@WebServlet(name = "FileServlet", urlPatterns = "/servlet/FileServlet") @MultipartConfig() public class FileServlet extends HttpServlet { private String getFileName(Part part) { String contentDisposition = part.getHeader("content-disposition"); String[] elements = contentDisposition.split(";"); for (String element : elements) { if (element.trim().startsWith("filename")) { return element.substring(element.indexOf('=') + 1).trim().replace("\"", ""); } } return null; } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Part part = request.getPart("uploadFile"); //提取文件域的part //System.out.println(request.getContextPath()); //System.out.println(request.getServletPath()); //System.out.println(request.getRequestURI()); //System.out.println(getServletContext().getRealPath("/upload")); String fileName = getFileName(part); if (fileName != null && !fileName.isEmpty()) { part.write(getServletContext().getRealPath("/upload") + "/" + fileName); //服务器中项目目录下的upload目录,绝对路径 } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }
多文件上传,只需在以上基础上,加一个遍历part,一个个分析即可。
三、第一次编写遇到的问题:
在Part的write方法上碰到了瓶颈,第一次我使用的是part.write(request.getContextPath() + "/upload/" + fileName),发现出现错误:
发现write的路径却是拼接了 request.getContextPath() + "/upload/" + fileName 路径。在路径跳转中如果路径开头有一个/,那么代表是绝对路径,而request.ContextPath()返回开头有斜杆,而write却将其作为相对路径,由此可以推测出,write只识别路径字符串,并不会像路径跳转那样识别斜杆,识别../这些。
然后我使用了 part.write(getServletContext().getRealPath("/upload") + "/" + fileName) ,毫无疑问这返回是项目在服务器中的绝对路径,但是却又出现错误:
发现保存路径是在IDEA的项目空间下,又想起IDEA在部署项目到tomcat的时候并不是直接部署,即你在tocmat的webapps下找不到部署的项目,详情见:http://www.cnblogs.com/chenloveslife/p/8727646.html。
由于出错是部署的项目(不是原工作空间的项目)中没有upload文件夹,然后我在IDEA的我的项目下的out的war_explode新增了upload文件夹(如果在工作空间的项目根下创建upload文件夹,并且该文件夹中有任意文件,那么在redeploy后,它也会部署到war_exploded,就无需直接在war_exploded中创建文件夹了),war_explode代表在tomcat中的项目,运行项目,路径正确并成功上传。
以上为基础的上传文件,有时还需要处理如果出现同名的文件应该如何处理,以及处理多个上传文件,以及控制上传文件大小,优化等等。