. 增强HttpServletResponse对象
1. 实现一个增强的HttpServletResponse类,需要继承javax.servlet.http.HttpServletRequestWrapper类,通过重写自己需要增强的方法来实现(这种模式就叫做装饰者模式),使用该增强类在加上过滤器就可以实现无编码转换处理代码。
public class MyRequest extends HttpServletRequestWrapper{ private HttpServletRequest req; public MyRequest(HttpServletRequest request) { super(request); req=request; } @Override public String getParameter(String name) { //解决编码问题,无论是post还是get请求,都不需要在业务代码中对编码再处理 String method=req.getMethod(); if("get".equalsIgnoreCase(method)){ try { String str=req.getParameter(name); byte[] b=str.getBytes("iso8859-1"); String newStr=new String(b, "utf-8"); return newStr; } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } }else if("post".equalsIgnoreCase(method)){ try { req.setCharacterEncoding("utf-8"); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //绝对不能删除此行代码,因为此行代码返回的就是编码之后的数据 return super.getParameter(name); } }
在过滤器中应用
public class FilterTest4 implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //生成增强的HttpServletRequest对象 HttpServletRequest req=(HttpServletRequest) request; MyRequest myReq=new MyRequest(req); //将增强的HttpServletRequest对象传入过滤器执行链中,在后面传入的request对象都会是增强的HttpServletRequest对象 chain.doFilter(myReq, response); } @Override public void destroy() {} }
2. 文件上传原理过程
1. JavaWeb中实现文件上传:
<html> <head> <title>My JSP 'upload.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> </head> <body> <form action="" method="post" enctype="multipart/form-data"> <input type="text" name="name"> 请选择文件:<input type="file" name="upload"> <input type="submit" value="上传"> </form> </body> </html>
import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; public class UploadServlet extends HttpServlet{ @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { /** * 1. 创建磁盘文件项工厂类 DiskFileItemFactory * 2. 创建核心解析Request类 ServletFileUpload * 3. 开始解析Request对象中的数据,并返回一个List集合 * 4. List中包含表单中提交的内容 * 5. 遍历集合,获取内容 */ DiskFileItemFactory fac=new DiskFileItemFactory(); ServletFileUpload upload=new ServletFileUpload(fac); upload.setHeaderEncoding("utf-8");//防止中文的文件名乱码 try { List<FileItem> fileItems = upload.parseRequest(req); for(FileItem item:fileItems){ //有可能是普通文本项,比如<input type="text">标签提交上来的字符串 //也有可能是<input type="submit" value="上传">上传的文件 //文件项与普通项有不同的API来处理 //首先判断是普通文本项还是文件项, if(item.isFormField()){ //true表示普通文本项 //获取文本项的name属性值 String name=item.getFieldName(); //获取对应的文本 String value=item.getString("utf-8");//防止中文乱码 System.out.println(name+":"+value); }else{ //false表示文件项 //先获取文件名称 String name=item.getName(); //获取文件项的输入流 InputStream in=item.getInputStream(); //获取服务器端文件存储的目标磁盘路径 String path=getServletContext().getRealPath("/upload"); System.out.println(path); //获取输出流,输出到本地文件中 OutputStream out=new FileOutputStream(path+"/"+name); //写入数据 int len=0; byte[] b=new byte[1024]; while((len=in.read(b))!=-1){ out.write(b,0,len); } in.close(); out.close(); } } } catch (FileUploadException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
注意:在文件上传时,会将form表单的属性enctype属性值为"multipart/form-data",当提交到服务端后,无法使用 req.getParameter(name) 方法来获取到内容,只有通过上面的方法来获取文本项。
2. 文件上传相关核心类:
//改进上面的文件上传代码,添加一个临时文件 public class UploadServlet extends HttpServlet{ @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { DiskFileItemFactory fac=new DiskFileItemFactory(); fac.setSizeThreshold(1024*1024);//设置缓冲区为1mb //设置临时文件的本地磁盘存储路径 File repository=new File(getServletContext().getRealPath("/temp")); fac.setRepository(repository); ServletFileUpload upload=new ServletFileUpload(fac); upload.setHeaderEncoding("utf-8");//防止中文的文件名乱码 try { List<FileItem> fileItems = upload.parseRequest(req); for(FileItem item:fileItems){ if(item.isFormField()){ String name=item.getFieldName(); String value=item.getString(); String value=item.getString("utf-8");//防止中文乱码 System.out.println(name+":"+value); }else{ String name=item.getName(); InputStream in=item.getInputStream(); String path=getServletContext().getRealPath("/upload"); System.out.println(path); OutputStream out=new FileOutputStream(path+"/"+name); int len=0; byte[] b=new byte[1024]; while((len=in.read(b))!=-1){ out.write(b,0,len); } in.close(); out.close(); } } } catch (FileUploadException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
3. 实现多文件上传(需要js技术):主要是更改jsp页面,通过js代码来添加多个文件进行上传,服务器代码无需更改
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8" contentType="text/html; charset=utf-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'upload.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> </head> <body> <script type="text/javascript"> function run(){ var div=document.getElementById("divId"); div.innerHTML+= "<div><input type='file' name='upload'><input type='button' value='删除' onclick='del(this)'></div>" } function del(presentNode){ var div=document.getElementById("divId"); div.removeChild(presentNode.parentNode); } </script> <div> 多文件上传<br/> <form action="/Servlet/upload" method="post" enctype="multipart/form-data"> <input type="button" value="添加" onclick="run()"><br/> <div id="divId"> </div> <input type="submit" value="上传"> </form> </div> </body> </html>
4. 关于文件上传的一些问题:
3. 文件下载
1. 传统文件下载方式有超链接下载或者后台程序下载两种方式。通过超链接下载时,如果浏览器可以解析,那么就会直接打开,如果不能解析,就会弹出下载框;而后台程序下载就必须通过两个响应头和一个文件的输入流。
2. 后台程序下载:
JSP指令用来设置整个JSP页面相关的属性,如网页的编码方式和脚本语言。
语法格式如下:
<%@ directive attribute="value" %>
指令可以有很多个属性,它们以键值对的形式存在,并用逗号隔开。
JSP中的三种指令标签:
指令 | 描述 |
---|---|
<%@ page ... %> | 定义网页依赖属性,比如脚本语言、error页面、缓存需求等等 |
<%@ include ... %> | 包含其他文件 |
<%@ taglib ... %> | 引入标签库的定义 |
Page指令
Page指令为容器提供当前页面的使用说明。一个JSP页面可以包含多个page指令。
Page指令的语法格式:
<%@ page attribute="value" %>
等价的XML格式:
<jsp:directive.page attribute="value" />
属性
下表列出与Page指令相关的属性:
属性 | 描述 |
---|---|
buffer | 指定out对象使用缓冲区的大小 |
autoFlush | 控制out对象的 缓存区 |
contentType | 指定当前JSP页面的MIME类型和字符编码 |
errorPage | 指定当JSP页面发生异常时需要转向的错误处理页面 |
isErrorPage | 指定当前页面是否可以作为另一个JSP页面的错误处理页面 |
extends | 指定servlet从哪一个类继承 |
import | 导入要使用的Java类 |
info | 定义JSP页面的描述信息 |
isThreadSafe | 指定对JSP页面的访问是否为线程安全 |
language | 定义JSP页面所用的脚本语言,默认是Java |
session | 指定JSP页面是否使用session |
isELIgnored | 指定是否执行EL表达式 |
isScriptingEnabled | 确定脚本元素能否被使用 |
Include指令
JSP可以通过include指令来包含其他文件。被包含的文件可以是JSP文件、HTML文件或文本文件。包含的文件就好像是该JSP文件的一部分,会被同时编译执行。
Include指令的语法格式如下:
<%@ include file="文件相对 url 地址" %>
include 指令中的文件名实际上是一个相对的 URL 地址。
如果您没有给文件关联一个路径,JSP编译器默认在当前路径下寻找。
等价的XML语法:
<jsp:directive.include file="文件相对 url 地址" />
Taglib指令
JSP API允许用户自定义标签,一个自定义标签库就是自定义标签的集合。
Taglib指令引入一个自定义标签集合的定义,包括库路径、自定义标签。
Taglib指令的语法:
<%@ taglib uri="uri" prefix="prefixOfTag" %>
uri属性确定标签库的位置,prefix属性指定标签库的前缀。
等价的XML语法:
<jsp:directive.taglib uri="uri" prefix="prefixOfTag" />
如您还有不明白的可以在下面与我留言或是与我探讨QQ群308855039,我们一起飞!
言:在CGI(通用网关接口)编程风行的日子,人们纷纷寻思简化并让其更加灵活的新方法和新技术,包括PHP、JSP、ASP。1997年SUN公司推出了servlet,1999年初,SUN公司推出了JSP,实际上在servlet基础上修改而成。JSP即Java服务器页面(Java Server Page),将HTML和JAVA代码使用某种方式结合起来,完成前后端的有效交互。本文通过JSP历史回顾和应用总结来审视过去和开拓未来。
其实,从现在看来JSP依然极具魅力,据了解很多JSP老项目还在维护。下面使用STS作为IDE构建一个实例项目,先体验其效果。
如下图,使用new dynamic web project创建项目:
进入preferences修改JSP文件的编码:
新增一个JSP文件到WebContent目录,如下
然后检查并修改编码,应该都是UTF-8,如下图:
如果不是,就修改。
选中项目名称,点邮件菜单的Run as ...-->Run on server
之后出现如下界面:
若没有现存的tomcat server,就自己加一个。点Finish。然后自动调用chrome,界面如下:
自动调用浏览器设置的地方是windows-->Web browser菜单:
(1)显示ip地址:getRemoteAddr
(2)变量声明<%! %>
(3)表达式<%=%>
语法 | 描述 |
<%-- 注释 --%> | JSP注释,注释内容不会被发送至浏览器甚至不会被编译 |
<!-- 注释 --> | HTML注释,通过浏览器查看网页源代码时可以看见注释内容 |
<\% | 代表静态 <%常量 |
%\> | 代表静态 %> 常量 |
\' | 在属性中使用的单引号 |
\" | 在属性中使用的双引号 |
指令 | 描述 |
<%@ page ... %> | 定义页面的依赖属性,比如脚本语言、error页面、缓存需求等等 |
<%@ include ... %> | 包含其他文件 |
<%@ taglib ... %> | 引入标签库的定义,可以是自定义标签 |
属性 | 描述 |
buffer | 指定out对象使用缓冲区的大小 |
autoFlush | 控制out对象的 缓存区 |
contentType | 指定当前JSP页面的MIME类型和字符编码 |
errorPage | 指定当JSP页面发生异常时需要转向的错误处理页面 |
isErrorPage | 指定当前页面是否可以作为另一个JSP页面的错误处理页面 |
extends | 指定servlet从哪一个类继承 |
import | 导入要使用的Java类 |
info | 定义JSP页面的描述信息 |
isThreadSafe | 指定对JSP页面的访问是否为线程安全 |
language | 定义JSP页面所用的脚本语言,默认是Java |
session | 指定JSP页面是否使用session |
isELIgnored | 指定是否执行EL表达式 |
isScriptingEnabled | 确定脚本元素能否被使用 |
行为标签语法:<jsp:行为名称 attribute="value" />
语法 | 描述 |
jsp:include | 用于在当前页面中包含静态或动态资源 |
jsp:useBean | 寻找和初始化一个JavaBean组件 |
jsp:setProperty | 设置 JavaBean组件的值 |
jsp:getProperty | 将 JavaBean组件的值插入到 output中 |
jsp:forward | 从一个JSP文件向另一个文件传递一个包含用户请求的request对象 |
jsp:plugin | 用于在生成的HTML页面中包含Applet和JavaBean对象 |
jsp:element | 动态创建一个XML元素 |
jsp:attribute | 定义动态创建的XML元素的属性 |
jsp:body | 定义动态创建的XML元素的主体 |
jsp:text | 用于封装模板数据 |
使用bean是最好用的行为标签,例如,我们创建一个java类:
然后,在JSP中使用useBea/setProperty/getProperty三个行为标签:
测试:
对象 | 描述 |
request | HttpServletRequest类的实例 |
response | HttpServletResponse类的实例 |
out | PrintWriter类的实例,用于把结果输出至网页上 |
session | HttpSession类的实例 |
application | ServletContext类的实例,与应用上下文有关 |
config | ServletConfig类的实例 |
pageContext | PageContext类的实例,提供对JSP页面所有对象以及命名空间的访问 |
page | 类似于Java类中的this关键字 |
Exception | Exception类的对象,代表发生错误的JSP页面中对应的异常对象 |
判断语句包括两种:if else和switch case
示例如下:
循环语句包括两种:for和while
示例如下:
类别 | 操作符 | 结合性 |
后缀 | () [] . (点运算符) | 左到右 |
一元 | ++ - - ! ~ | 右到左 |
可乘性 | * / % | 左到右 |
可加性 | + - | 左到右 |
移位 | >> >>> << | 左到右 |
关系 | > >= < <= | 左到右 |
相等/不等 | == != | 左到右 |
位与 | & | 左到右 |
位异或 | ^ | 左到右 |
位或 | | | 左到右 |
逻辑与 | && | 左到右 |
逻辑或 | || | 左到右 |
条件判断 | ?: | 右到左 |
赋值 | = += -= *= /= %= >>= <<= &= ^= |= | 右到左 |
逗号 | , | 左到右 |
布尔值(boolean):true 和 false;
整型(int):与 Java 中的一样;
浮点型(float):与 Java 中的一样;
字符串(string):以单引号或双引号开始和结束;
Null:null。
以下提供一个例子来说明:
(1)创建FormProcess.jsp文件:
其中提供了两个input输入框。
(2)再创建FormProcessMain.jsp文件,如下:
其中使用request对象的getParameter方法来获取Get参数。
JSP过滤器实际上和Servlet过滤器一样。
以下通过示例来说明:
(1)在web.xml中增加一个过滤器:
(2)然后创建过滤器处理类com.hunting.LogFilter,如下:
(1)核心标签:
标签 | 描述 |
<c:out> | 用于在JSP中显示数据,就像<%= ... > |
<c:set> | 用于保存数据 |
<c:remove> | 用于删除数据 |
<c:catch> | 用来处理产生错误的异常状况,并且将错误信息储存起来 |
<c:if> | 与我们在一般程序中用的if一样 |
<c:choose> | 本身只当做<c:when>和<c:otherwise>的父标签 |
<c:when> | <c:choose>的子标签,用来判断条件是否成立 |
<c:otherwise> | <c:choose>的子标签,接在<c:when>标签后,当<c:when>标签判断为false时被执行 |
<c:import> | 检索一个绝对或相对 URL,然后将其内容暴露给页面 |
<c:forEach> | 基础迭代标签,接受多种集合类型 |
<c:forTokens> | 根据指定的分隔符来分隔内容并迭代输出 |
<c:param> | 用来给包含或重定向的页面传递参数 |
<c:redirect> | 重定向至一个新的URL. |
<c:url> | 使用可选的查询参数来创造一个URL |
(2)格式化标签:
标签 | 描述 |
<fmt:formatNumber> | 使用指定的格式或精度格式化数字 |
<fmt:parseNumber> | 解析一个代表着数字,货币或百分比的字符串 |
<fmt:formatDate> | 使用指定的风格或模式格式化日期和时间 |
<fmt:parseDate> | 解析一个代表着日期或时间的字符串 |
<fmt:bundle> | 绑定资源 |
<fmt:setLocale> | 指定地区 |
<fmt:setBundle> | 绑定资源 |
<fmt:timeZone> | 指定时区 |
<fmt:setTimeZone> | 指定时区 |
<fmt:message> | 显示资源配置文件信息 |
<fmt:requestEncoding> | 设置request的字符编码 |
(3)SQL标签:
标签 | 描述 |
<sql:setDataSource> | 指定数据源 |
<sql:query> | 运行SQL查询语句 |
<sql:update> | 运行SQL更新语句 |
<sql:param> | 将SQL语句中的参数设为指定值 |
<sql:dateParam> | 将SQL语句中的日期参数设为指定的java.util.Date 对象值 |
<sql:transaction> | 在共享数据库连接中提供嵌套的数据库行为元素,将所有语句以一个事务的形式来运行 |
(4)XML标签:
标签 | 描述 |
<x:out> | 与<%= ... >,类似,不过只用于XPath表达式 |
<x:parse> | 解析 XML 数据 |
<x:set> | 设置XPath表达式 |
<x:if> | 判断XPath表达式,若为真,则执行本体中的内容,否则跳过本体 |
<x:forEach> | 迭代XML文档中的节点 |
<x:choose> | <x:when>和<x:otherwise>的父标签 |
<x:when> | <x:choose>的子标签,用来进行条件判断 |
<x:otherwise> | <x:choose>的子标签,当<x:when>判断为false时被执行 |
<x:transform> | 将XSL转换应用在XML文档中 |
<x:param> | 与<x:transform>共同使用,用于设置XSL样式表 |
下面从前后端分离角度展现JSP的产生和发展脉络。
1957年美国国防部(DoD)组建了高级研究计划局(ARPA)。1961年7月,MIT工程师Leonard Kleinrock发表Information Flow in Large Communication Nets论文。1962年8月, MIT工程师J.C.R. Licklider和W. Clark发表On-Line Man Computer Communication论文。1967 2月,在ARPA IPTO PI会议上,Larry Roberts组织了有关ARPANET设计方案的讨论。互联网雏形ARPANET由此产生。
互联网最初的目的是访问和复制文件从一台计算机到另一台远程计算机,两台计算机之间有网络,但速度慢,而且经常专用于大学或者研究机构是昂贵的,不支持大规模用户。1970年,ARPANET主机开始使用网络控制协议(NCP),这就是后来的传输控制协议(TCP)的雏形。
人们基于TCP/IP构建了更快的网络,其中应用层文件传输协议(FTP)规范提供了交换这些文件的标准方法。1973年,文件传输协议(FTP)推出,用于在异构系统之间交换文件。FTP传输的文件并不能有效查看,因此发明了超文本标记语言(HTML),使我们能够在互联网上看到文档。
1989年蒂姆伯纳斯-李写了一份关于建立一个通过网络传输超文本系统的报告,其中创建了单行 HTTP 协议,这个协议在1991年被命名为HTTP/0.9,万维网由此产生,1996年超文本传送协议HTTP 1.0发布。
尽管FTP可以传输HTML文件,但是并没有提供有效小文件传输机制和无状态管理能力,超文本传输协议(HTTP)才被发明出来专门用于传输HTML文档,HTTP是一种无连接/无状态协议,这使得许多短连接更加有效,而且可以不使用密码就可以获取HTML文档,这让万维网真正到来,真正体现了互联网的快捷、免费思维。
上面提到了TCP/IP,HTTP,FTP都是通信管道,后来大家把注意力转向内容。我们感兴趣的文件随着IT技术的发展而快速爆发,例如数据库快速发展导致各种应用系统快速发展。同时,数据的更新频度更加快速,这种即时信息推动了互联网流量快速增长。通过基于HTTP的HTML,最终用户可以浏览位于远程服务器上的文件。
当时,远程服务器属于局域网,局域网连接到互联网需要专用设备,可惜的是这种专用设备并不好用(直到后来路由器和交换机被思科发明出来),因此通用网关接口(CGI)规范被制定出来,它允许Web服务器超越文件服务器并从内部数据库中获取数据,并动态更改HTML。
最初,CGI是在1993年由美国国家超级电脑应用中心(NCSA)为NCSA HTTPd Web服务器开发的,当年NCSA内部有一份简单的CGI规范说明,后来在1997年Ken Coar领导的团队制定了CGI1.1规范,并提交RFC。CGI规范是Web应用程序开发中的一个重大突破,确保了相同的CGI程序在不同的Web服务器上工作。
CGI成为传递动态内容的最常用手段。只是互联网发展太快了,CGI的性能无法跟上,每个对CGI脚本的请求都会产生一个单独的进程。这种设计让CGI在高峰负载时也消耗大量资源。解决方案非常多,功能性和可伸缩性成为关键。
许多CGI替代者都采用服务器端编程技术来实现业务逻辑,包括ASP、ColdFusion、PHP和Perl等等,其中至今仍然在大量使用的是PHP ,PHP 继承自一个老的工程,名叫 PHP/FI,PHP/FI 在 1995 年由 Rasmus Lerdorf 创建,最初只是一套简单的 Perl 脚本,用来跟踪访问他主页的人。然后,即使是PHP,最终由于可移植性和面向对象的编程设计,Java超越了PHP和其他CGI平台。
Java自1991年诞生以来已经走过了很长的一段路,当时Sun公司推出了“绿色计划”,试图集成数字消费设备,如电视机、CD播放机和计算机。OAK(名字来自高斯林窗外的一棵橡树!)诞生了,但直到出现HotJava和小程序,才开始活跃起来。1995年,Sun发布了开源Java,向微软发起了挑战,反响巨大。这促使Java深入到服务器端开发领域。
Sun在Java中加入了Internet功能,并在1997年6月发布了servlet接口。servlet以CGI替代为目标。与CGI为每个请求启动一个进程不同,servlet使用更细粒度的线程在单个进程中运行。servlet采用了更有效的体系架构,能够应对互联网上的复杂情况。Java servlet为开发Java Web组件提供了基础。servlet优点是每个额外的并发请求带来的额外开销非常小。
servlet技术需要真正的Java编程技能才能有效应用,外观和良好体验为web应用带来了巨大方便,但是图像支持还不是那么良好。于是Sun公司在1998年发布了JavaServer Pages (JSP),这来自于微软ASP的启发,也有些人说是复制的,它使得编写动态HTML页面变得容易。
使用JSP的使用非常简单,有些工具(例如Dreamweaver)能让非程序员来构建WWW网站前端页面,当然要servlet带动后端服务器代码(例如javabean)才能完成完整的WWW网站构建工作,这样构建的WWW网站具有模块化、可维护、可伸缩和可移植优点,从而完成简单网站到复杂Web应用程序的转变,从而实现前后端分离。
JSP官方版本1.0和1.1都出现在1999年,都很受欢迎,版本1.2出现于2001年,是目前最流行的实现。
JSP终究还是含有Java代码,前后端没有彻底分离,因此人们在2009年发明了node-js,这让前端开发人员崛起,他们单纯使用HTML+CSS+JavaScript前端语言就能完成前端页面的开发,而不需要使用含有各种后端交互印记的标签。
于是,基于node-js的React、Angela、VUE框架成为潮流。
*请认真填写需求信息,我们会在24小时内与您取得联系。