文件上传能够说是Web应用中很经常使用的一块,前几天打算研究一下HTML5提供的FileReader API,而且用Tomcat做为后端来实验大文件的上传(只是学校的课程做业必须用Java写,都不容许使用最好的编程语言php>.<)。可Java Servlet与php这种喜闻乐见的Web码农语言不一样,并无提供一个很简单的处理文件上传的API,因此还捣鼓了蛮久,也对通常的文件上传的HTML控件和实现原理稍微有了一点了解。php
对面宿舍的一位同窗说我好久没更了不太好,因而我就写一篇,谢谢他的提醒。html
首先,咱们都知道最多见的HTML的文件上传控件是喜闻乐见的<input type="file">,但必定要搭配form的属性enctype="multipart/form-data",服务器上要有一个接收上传的cgi或者别的什么,既然咱们用java写,就叫uploadServlet。因而有了一个以下的常见的上传表单。java
<form action="uploadServlet" enctype="multipart/form-data" method="POST"> <input name="password" type="password" /> <input name="File1" type="file" /> <input type="submit" value="Upload" /> </form>
而后咱们在后端处理。因为Java Servlet的API是没有提供什么$_FILES数组这样傻瓜式的文件操控方式,咱们必须本身处理request。咱们不妨先把收到的request输出到文件当中,看看Servlet会收到什么,再想一想怎么处理。放这样一个servlet的代码:web
import javax.servlet.*; import javax.servlet.http.*; import java.io.*; public class UploadServlet extends HttpServlet{ @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ response.setContentType("text/plain;charset=utf-8"); PrintWriter writer=response.getWriter(); InputStream in=request.getInputStream(); File f = new File("/tmp/upload"); //把文件存到/tmp/upload FileOutputStream fout = new FileOutputStream(f); byte[] b=new byte[1024]; int n=0; while ((n=in.read(b))!=-1){ fout.write(b,0,n); } fout.close(); in.close(); writer.println("Finished uploading files!"); writer.close(); } }
有了Servlet就拖出去跑一跑。这里个人表单不只会发送文件,还会发送一个密码域。若是我随便发一个文本文件,那么我获得了这样的结果。
多上传几回还会发现那一堆横杠开头的数字会变更。这下很差玩了,虽然咱们能够看到咱们上传的数据,但要解析它有点过于复杂了。这个请求是依据RFC1867来写的,虽然有标准可依,但咱们这么懒怎么会去依照标准写一个解析器呢?apache
因而咱们须要请出Apache开发的文件上传处理库Commons FileUpload。这个网站提供了最新版的下载连接和基本的使用指南。文档讲得过于全面了,而咱们通常不须要那么多功能,够用就好。翻了几篇教程,写出来了一个简单的文件上传接收代码。编程
javaimport javax.servlet.*; import javax.servlet.http.*; import java.io.*; import java.util.*; import org.apache.commons.fileupload.*; import org.apache.commons.fileupload.servlet.*; import org.apache.commons.fileupload.disk.*;
首先须要多装载三个库,以及一个java.util.List,由于到时候处理的时候,Commons FileUpload会把搞成一个List返回回来,咱们须要接收这个List并处理解析它。后端
public class UploadServlet extends HttpServlet{ private String filepath; private String temppath; private String buf; public void init(ServletConfig config) throws ServletException{ super.init(config); ServletContext context=getServletContext(); filepath=context.getRealPath("/"+config.getInitParameter("filepath")); temppath=context.getRealPath("/"+config.getInitParameter("temppath")); }
为了方便维护,我把保存上传文件的目录用Init Parameter的方式写到web.xml里面去,而后在这个地方读出来。咱们须要一个保存上传文件的目录和一个用来作缓存的临时目录。若是你接收上传文件以后不打算保存而是直接拿去处理,也没有问题,可是必定要有一个缓存目录,在后面有用。数组
接下来是真正激动人心的处理上传的代码了。我懒得写doGet了因此就只有一个doPost。缓存
@Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ response.setContentType("text/plain;charset=utf-8"); PrintWriter writer=response.getWriter(); int count=0; try{ DiskFileItemFactory diskFactory = new DiskFileItemFactory(); diskFactory.setSizeThreshold(4 *1024 ); diskFactory.setRepository(new File(temppath));
这里咱们开了一个diskFactory,就是FileUpload所须要使用的缓存,当内存存不下上传的文件的时候,它会自动写入缓存目录。经过setSizeThreshold方法能够设置内存的使用上限,也就是当内存用了这么多却还存不下,就开始写缓存。显然这个值很大程度上会决定这个Servlet的效率。服务器
ServletFileUpload upload = new ServletFileUpload(diskFactory); upload.setSizeMax(4 * 1024 * 1024); List fileItems = upload.parseRequest(request); Iterator iter = fileItems.iterator();
这里咱们就真正建了一个ServletFileUpload的实例upload来处理文件的上传。能够设置上传文件的最大大小。而后把request对象直接交给upload来解析,它会返回一个一个List,这个List的每一项其实是一个FileItem对象,后面就要用迭代器处理这个列表。
while (iter.hasNext()){ FileItem item = (FileItem) iter.next(); if (item.isFormField()){ writer.println(item.getFieldName()+" : "+item.getString()); }
要注意的是ServletFileUpload也会处理非文件的信息,能够用isFormField方法来检查,而后将信息获取出来。但这在这里不是重点,只是必需要处理掉而已。
else{ String filename = item.getName(); filename = filename.substring( filename.lastIndexOf("\\")+1,filename.length()); File uploadFile = new File(filepath+"/"+filename); item.write(uploadFile); writer.println("Get file:"+ filename); writer.println(" filetype: "+item.getContentType()); count++; } } } catch (Exception e){ e.printStackTrace(); } writer.println("Finished uploading files!"); writer.close(); } }
处理文件的代码就这么点,若是还要说什么的话,就是每一个文件的FileItem对象不只能够用write方法来直接写到什么文件里面去,也能够用getInputStream方法获得一个输入流来解析,或者用get方法直接读到一个byte数组里面去。能够说这个库提供了一个很方便的方法解析上传的文件。
最后提一下,Apache Commons是一个Java加强库,提供了大量的优质Java资源库,涉及不少开发领域。若是不出意外,应该我会在近期写一篇关于JavaScript FileReader的blog,敬请期待。