伴随着JDK1.6一起出现的Servlet3.0标准,使得JSP的文件上传系统不再艰难,此前在JSP的文件上传系统需要《【Jsp】使用jspsmartupload完成简单的文件上传系统》(点击打开链接)类似这样的插件才能完成的文件上传系统,还不支持中文,使得各位程序猿掏空心思才能解决这个问题。现在Servlet3.0对文件上传的方法进行封装,无须分块就可以实现。而且Servlet3.0还不用类似《【Servlet】最简单的Servlet JavaWeb程序》(点击打开链接)在web.xml里面各种配置,一个小小的Annotation就能完成以前在.xml的多行代码。
一、基本目标
实现如下的一个文件上传系统,上传之后显示文件类型、文件大小、文件名、文件后缀名,如果上传的是图片,则显示,无比健壮。大概唯一的缺点就是没有进度条……上传进度条也是一个大工程,以后再搞。而且编写出来的JSP页面符合Model2标准,在上传页面与上传成功显示页面,没有任何JSP代码,仅有JSTL表达式与JSP2.0自身的取值表达式。如果不想跳转可以参考《【Jsp】使用AjaxFileUploader与jspsmartupload完成不刷新的Ajax文件上传系统》(点击打开链接)的内容。
如果你直接访问我的上传文件Servlet,弹出提示。
上传完,文件保存在服务器上的/upload文件夹里面,服务器一般不存中文文件名的,每一个文件名是时间戳,多用户上传还可以加上取走用户的ID并接文件名,时间戳的生成可以看我之前写的《【Java】有关System.currentTimeMillis()的思考》(点击打开链接)
二、基本准备
这个利用Servlet3.0标准与JSTL表达式实现上传系统的WEB工程目录结构如下所示:
首先在WebContent,如果是Myeclipse则是WebRoot网站根目录文件夹,下面新建一个upload文件夹,用来摆上传文件的。
web.xml文件则如《【Javaweb】Eclipse for JavaEE新建的Web工程自动生成web.xml》(点击打开链接)让Eclipse自己建可以,里面不用写任何东西了。精简之后如下所示:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> </web-app>然后再lib文件夹下面放入Servlet3.0的支持包javax.servlet-api-3.1.0.jar,JSTL表达式的支持包jstl.jar与standard.jar,这些东西都是JSP官方自己开发的东西和Tomcat一样,高版本的Tomcat还会自带,但是为了兼容所有版本的Tomcat还是要放的。这些东西自己上网搜一下就可以了。JSTL表达式的支持包jstl.jar与standard.jar要去搜索jakarta-taglibs-standard-1.1.2.zip,下载之后解压如下图所示,把lib里面的东西都取走。
三、制作过程
1、首先是最简单,只要学过HTML都会的Fileupload.jsp,就一个带文件表单,注意其中的写法,当时讲解前端的时候,文件表单或许提到过一下,注意除了post与action属性,还有enctype="multipart/form-data",表明这是传递文件的,文件表单必须配合服务器语言aspx,jsp,php三者之一,跑在服务器上面才有效的:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>FileUpload</title> </head> <body> <form method="post" action="upload" enctype="multipart/form-data"> 选择文件:<input type="file" name="file" /><br /> <input type="submit" value="上传" /> </form> </body> </html>
2、之后是Upload.java,来到了Servlet3.0,就再也不用搞web.xml了,可以利用Part对象接住整个上传过来的文件,加一句response.setCharacterEncoding("utf-8");解码就能支持中文了,如果利用拦截器实现全局解码,那么这句话也可以省了,可以参考我之前的《【Filter】利用过滤器Filter解决post传递的编码问题与利用EL表达式简化参数传递》( 点击打开链接),剩下只是切割字符串的问题了,另外设置一个动态数组arraylist配合后面的JSTL表达式的,动态数组arraylist不懂可以看《【Java】Java中的Collections类——Java中升级版的数据结构》( 点击打开链接 ),同时保存服务器的文件名是时间戳,一切内容的传递利用request容器,这容器请求之后就消息,也就是说request容器跳转前出生,跳转完毕页面加载完,request容器就立即死亡,具体看注释。
//有文件就有IO,util是设置一个arraylist配合后面的JSTL表达式的 import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; //这样设置就可以取代以前的web.xml配置Servlet的方式,但要注意引入Annotation包 @WebServlet(name="upload",urlPatterns={"/upload"}) //这是一个处理文件的Servlet @MultipartConfig public class Upload extends HttpServlet { // 防止用户直接输入网址访问此Servlet protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintStream out = new PrintStream(response.getOutputStream()); response.setContentType("text/html;charSet=utf-8"); out.print("请正常打开此页"); } //文件一般用doPost方法 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //解码 response.setCharacterEncoding("utf-8"); //把name为file的<input type="file" name="file" />里面选择的文件传递过来 Part part=request.getPart("file"); //这样就能够取到上传文件名,不为什么, //content-disposition前面35个字符是form-data文件表单名name="file" //每一个上传的文件都是固定的 String filename=part.getHeaders("content-disposition").toString().substring(35).replace("\"]", ""); //对文件名从.开始截取就能取到文件的后缀名 String fileExtensions=filename.substring(filename.lastIndexOf(".")); //设置一个动态数组FileInfo,不停把文件信息放进去 ArrayList<String> FileInfo=new ArrayList<String>(); FileInfo.add("文件类型:"+part.getContentType()); FileInfo.add("文件大小:"+part.getSize()/1024+"kb"); FileInfo.add("文件名:"+filename); FileInfo.add("后缀名:"+fileExtensions); //为文件的后缀名独立设置一个参数 request.setAttribute("fileExtensions",fileExtensions); //这个是服务器上面的文件名,利用System.currentTimeMillis()时间戳 //后面补个+""就能把long强制toString()了 String ServersFilename=System.currentTimeMillis()+""; //动态数组FileInfo同时也保存这个在服务器的名字 //用于后续组成图片显示的地址 FileInfo.add(ServersFilename); //之后,把动态数组FileInfo放进容器 request.setAttribute("FileInfo",FileInfo); //如果文件名是以图片后缀名结尾的 //当然你也可以里面上面的fileExtensions用equal方法来判断 //记得字符串是对象,千万不要用==去比较就可以了 if(filename.endsWith(".jpg")||filename.endsWith(".gif")||filename.endsWith(".bmp")||filename.endsWith(".png")){ //在于request设置一个属性hasPic,告诉下一页是否图片 request.setAttribute("hasPic","true"); } else{ request.setAttribute("hasPic","false"); } //把文件保存到服务器getServletContext().getRealPath("/upload")能取到服务器/upload的目录 //不带参数则是根目录 //之后就是保存到服务器的名字与文件的后缀名并接起来就可以了 part.write(getServletContext().getRealPath("/upload")+"/"+ServersFilename+fileExtensions); //之后带着request容器跳转到UploadSuccess页面 request.getRequestDispatcher("/UploadSuccess.jsp").forward(request,response); } }还有一点补充的是:part.getSize()方法取出来的文件大小是以bit为单位,请自己除以1024,或者1024的平方去换算。
3、上传成功显示页面UploadSuccess.jsp,这一页还肩负着显示图片的任务,同时使用到了JSTL表达式,记得在本页头部声明要是用C标签,具体如下:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!-- 表明我要使用JSTL表达式! --> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Success</title> </head> <body> <%-- <c:choose>表示一个条件结构配合<c:when>与<c:otherwise>相当于if..else结构 --%> <c:choose> <!-- 先判断hasPic是否是true,用户是不是上传了一张图片? --> <c:when test="${requestScope.hasPic=='true'}"> <%-- <c:forEach>就是循环结构 --%> <!-- 遍历request容器里面的动态数组FileInfo,其中每一项的以fileinfo表示 --> <!-- 类似php的foreach与Jdk1.5的新型遍历 --> <!-- fileinfoStatus旗下有很多类成员用来判断状态的 --> <c:forEach var="fileinfo" items="${requestScope.FileInfo}" varStatus="fileinfoStatus"> <c:choose> <!-- 因为动态数组FileInfo的最后一项正是服务器上面的图片的文件名! --> <c:when test="${fileinfoStatus.last}"> <!-- 因此如果遍历到这一项,就upload/+服务器上面的图片的文件名+文件的后缀名并接其一个图片地址,用img标签显示 --> <img src="upload/<c:out value="${fileinfo}" /><c:out value="${requestScope.fileExtensions}" />" /> </c:when> <c:otherwise> <!-- 不然就逐一输出文件信息咯! --> <c:out value="${fileinfo}" /> <br /> </c:otherwise> </c:choose> </c:forEach> </c:when> <!-- 如果不是图片 --> <c:otherwise> <!-- 那么就显示动态数组FileInfo中的0-3项,逐一显示 --> <!-- 其实不写,也是默认显示整个数组,主要是为了说明begin,end,step等各个参数 --> <!-- step的意思就是逐X个显示,每X次显示一次的意思 --> <c:forEach var="fileinfo" items="${requestScope.FileInfo}" begin="0" end="3" step="1"> <c:out value="${fileinfo}" /> <br /> </c:forEach> </c:otherwise> </c:choose> </body> </html>
做到这里,整个利用Servlet3.0标准与JSTL表达式,支持图片上传后显示的文件上传系统,就做完了。
各位体现到Servlet3.0标准与JSTL表达式的Model 2的标准的优越性了吗?
当然又有各种SSH大神准备喷我了……