【Servlet】利用Servlet3.0标准与JSTL表达式实现文件上传系统,支持图片上传后显示

时间:2021-08-19 21:02:15

伴随着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】利用Servlet3.0标准与JSTL表达式实现文件上传系统,支持图片上传后显示

如果你直接访问我的上传文件Servlet,弹出提示。

【Servlet】利用Servlet3.0标准与JSTL表达式实现文件上传系统,支持图片上传后显示

上传完,文件保存在服务器上的/upload文件夹里面,服务器一般不存中文文件名的,每一个文件名是时间戳,多用户上传还可以加上取走用户的ID并接文件名,时间戳的生成可以看我之前写的《【Java】有关System.currentTimeMillis()的思考》(点击打开链接

【Servlet】利用Servlet3.0标准与JSTL表达式实现文件上传系统,支持图片上传后显示


二、基本准备

这个利用Servlet3.0标准与JSTL表达式实现上传系统的WEB工程目录结构如下所示:

【Servlet】利用Servlet3.0标准与JSTL表达式实现文件上传系统,支持图片上传后显示

首先在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里面的东西都取走。

【Servlet】利用Servlet3.0标准与JSTL表达式实现文件上传系统,支持图片上传后显示


三、制作过程

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大神准备喷我了……