编程语言 : Java的动态Web解决方案泛谈
文章概述
最近发现很久前一股脑地学习框架,发觉越发迷糊.知道了框架只是暂时的,重点是基础的技术.该文大篇幅回顾Servlet技术栈和简要的MVC框架.
至于为什么学J2EE,额,大家都用框架,可框架也是使用了标准的J2EE规范进行开发,比如SpringMVC的前端控制器是Servlet,Struts的Filter,Spring Boot项目内嵌了Tomcat 应用容器....
该文是自我学习总结,比较适合接触Java Web编程不久的朋友阅读,如果读的没意思就请直接弃之 :)
MVC framework
我知道,我明白你知道MVC框架,可是我还是叨唠一下.
Model,Java普通类对象,用来作为信息存储对象的模块.
View,服务器响应客户端请求后生成页面响应对象的模块.
Controller,处理信息类型转换以及执行业务的模块.
简单地说,就是将我们上传的信息与类型数据进行匹配转换,之后都是琐碎的加些什么数据拦截器,过滤器之类的.
因为我们大多数情况下通过一张表单打到服务器,这时表单的数据都默认是String类型的数据,这时就不适应于类型数据工程语言(C++,PHP,Java,C#).
所以,必须转换类型.
那么,有什么技术可以让我们获取表单数据?以及获取后我们该处理?
HTTP协议说了什么
HTTP协议就是一种让我们获取和返回数据的技术
关于这方面的知识建议你去看看《图解HTTP》,一本薄薄的书.这里只是作为引子做个简单的说明.
HTTP建立于TCP协议之上,但其实可以根据分层而选择忽略底层原理.
HTTP规定了应用层的请求响应规则,客户端<-->服务端的信息必须满足HTTP格式.
客户端浏览器请求,
服务端响应请求,
把HTML,CSS,Javascript等信息存储于HTTP对象载体,
响应返回至客户端,
客户端进行页面渲染显示.
Java动态web解决方案
Sun公司成为制定Java语言的先行者,使Java语言适用于多种领域开发,动态Web开发领域同样也给出了优秀的解决方案.
动态web技术--服务器根据客户端不同的请求数据来生成不同的响应数据并作返回.
Servlet体系
关于Servlet你需要弄清楚下面几个概念,你将在阅读完该文后掌握它们.
上面这张图是Java Web体系的原型技术,也就是说其他技术基本都构建在这些技术之上,它们是根基.
HTML,CSS,JavaScript三者作为页面渲染交互技术而存在;
JDBC作为连接数据库的连接技术,这样可以进行数据库信息的获取和存储;
Tomcat作为Servlet应用容器而存在,等待用户请求;
Servlet作为动态信息的Java处理类,能够将对应的Java数据结构转化为String拼接到HTML之中去;
JavaBean就是简单的Java类,它有固定的格式,很容易就能写出一个JavaBean;
Session存在是因为HTTP是无状态协议,需要Session来作为状态标示;
Request/Response是Servlet容器抽象出来的请求/响应对象,以它来获取数据和将数据写入HTTP响应;
JDBC
Java DataBase Connectivity是Java技术的核心之一,现在基本没有不连接数据库的web应用.
JDBC是一套Java定义的数据库连接接口,实现部分由各大数据库厂商进行开发.(你想要更大的市场,你就必须支持我)
软件开发其中一项精髓是抽象,暂时搁置实现细节,拿来用就行了,除非你要去做该类产品的实现.
JDBC编码步骤
1.Java语言有接口,但是没有提供实现,所以我们必须先加载实现包.
2.通过连接管理器注册驱动
3.获取数据库连接
4.得到代表SQL语句的对象
5.执行语句和获取结果
6.释放占用的资源
JDBC关键接口
DriverManager
①注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());//依赖具体的驱动类,会导致驱动被注册两次
Class.forName("com.mysql.jdbc.Driver");//替代方案,在类被引入时自动注册.
②获取数据库连接
DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password");
Connection
①获取数据库操作语句对象
conn.createStatement();
conn.prepareCall(sql);
conn.prepareStatement(sql,columnNames);
②事务处理
conn.commit();
conn.rollback();
Statement
①代表SQL语句
Statement s = conn.createStatement();
②执行语句,接收返回结果
ResultSet rs = s.executeQuery(sql);
s.executeUpdate(sql);
ResultSet
①查询SQL所返回的结果集对象,用来遍历操作
rs.next();
②遍历后是一条记录,可以获取记录上的数据
PrepareStatement
①优于Statement,指示SQL语句的预编译,提高数据库执行效率
②防止SQL注入,直接对象对接语句
③语句参数使用占位符?
JDBC的代码规范
1.配置文件
2.工具类
3.业务代码
分页
在web开发中,数据量分页的情况数不胜数,不同数据库分页语句不同,但是逻辑是一样的.
分页逻辑需要参数:数据总条数count(*),分页大小size,当前页面数current
Oracle
oracle中数据表中隐含了一个rownum字段,标识了每条记录在表中的行号,利用它能获取特定行数据.
select a1.* from (select student.*,rownum rn from student) a1 where rn between 3 and 5;
MySQL
MySQL中使用limit关键字来获取数据行数
select * from customer limit 10,5;//第10行开始后的前5条数据
Page类
分页逻辑实现
JDBC的其他重点
JDBC还有其他一些重点知识,包括存储过程调用,事务控制,数据库连接池实现等.由于篇幅问题不作详述,用到的时候直接查一下资料就能找到.
Servlet技术窥探
前面理解了Servlet是一个特殊的Java类,通过应用服务器运行来处理请求信息,下面应该熟悉下面的两张图
Servlet核心类:
这个类图在后面将反复使用,请查看手册,看一下方法名.
下面看一下流程图:
看4,7,8每次你访问服务器时,
Tomcat查询web.xml,查找url-pattern.
服务器会生成一个Request对象和一个Response对象来承接请求信息和响应信息,
每次访问会调用一次Servlet.service(),这个方法的逻辑就是整个响应请求的逻辑.
web应用配置
web app一般都会需要一些配置文件,在Java web应用中这个文件叫web.xml,它是用来配置该web app的.
Servlet声明及映射就是配置URL映射的标签,服务器查询标签知道我调的url是Servlet A还是Servlet B处理.
Servlet.service(req,resp)
Servlet可以由应用服务器生成,默认生成一个DefaultServlet,或者是开发者指定一个继承HttpServlet的类.
所有HTML,CSS,JavaScript等没有指定Servlet的都将默认生成一个DefaultServlet来进行处理.
每个访问都会调用一次Servlet.service(),
- /**
- * Receives standard HTTP requests from the public
- * <code>service</code> method and dispatches
- * them to the <code>do</code><i>Method</i> methods defined in
- * this class. This method is an HTTP-specific version of the
- * {@link javax.servlet.Servlet#service} method. There's no
- * need to override this method.
- *
- * @param req the {@link HttpServletRequest} object that
- * contains the request the client made of
- * the servlet
- *
- * @param resp the {@link HttpServletResponse} object that
- * contains the response the servlet returns
- * to the client
- *
- * @exception IOException if an input or output error occurs
- * while the servlet is handling the
- * HTTP request
- *
- * @exception ServletException if the HTTP request
- * cannot be handled
- *
- * @see javax.servlet.Servlet#service
- */
- protected void service(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- String method = req.getMethod();
- if (method.equals(METHOD_GET)) {
- long lastModified = getLastModified(req);
- if (lastModified == -1) {
- // servlet doesn't support if-modified-since, no reason
- // to go through further expensive logic
- doGet(req, resp);
- } else {
- long ifModifiedSince;
- try {
- ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
- } catch (IllegalArgumentException iae) {
- // Invalid date header - proceed as if none was set
- ifModifiedSince = -1;
- }
- if (ifModifiedSince < (lastModified / 1000 * 1000)) {
- // If the servlet mod time is later, call doGet()
- // Round down to the nearest second for a proper compare
- // A ifModifiedSince of -1 will always be less
- maybeSetLastModified(resp, lastModified);
- doGet(req, resp);
- } else {
- resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
- }
- }
- } else if (method.equals(METHOD_HEAD)) {
- long lastModified = getLastModified(req);
- maybeSetLastModified(resp, lastModified);
- doHead(req, resp);
- } else if (method.equals(METHOD_POST)) {
- doPost(req, resp);
- } else if (method.equals(METHOD_PUT)) {
- doPut(req, resp);
- } else if (method.equals(METHOD_DELETE)) {
- doDelete(req, resp);
- } else if (method.equals(METHOD_OPTIONS)) {
- doOptions(req,resp);
- } else if (method.equals(METHOD_TRACE)) {
- doTrace(req,resp);
- } else {
- //
- // Note that this means NO servlet supports whatever
- // method was requested, anywhere on this server.
- //
- String errMsg = lStrings.getString("http.method_not_implemented");
- Object[] errArgs = new Object[1];
- errArgs[0] = method;
- errMsg = MessageFormat.format(errMsg, errArgs);
- resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
- }
- }
根据HTTP请求的方法不同,调用相应的处理方法,现在一般只会使用到两种请求方法,GET/POST
对应这个请求的Servlet,将调用doGet(req,resp),以此类推.
至此,我们知道了关键的一点,关于Servlet编程,我们只要继承HttpServlet,重写doGet和doPost方法等待调用就行了.
该怎么重写呢?思路是,表单的提交信息一定是封装至HttpServletRequest对象中,我们通过获取信息后根据信息写入至HttpServletResponse对象.
HttpServletRequest.getParameter(String name)可以获取表单信息,处理信息.
HttpServletResponse.getWriter()可以获取到一个PrintWriter对象,可以将处理后相应的信息存储在这个对象中,然后由服务器处理写入HTTP报文主体中.
JSP视图
PrintWriter是可以完成输出操作,但是内容很是繁琐,上图简单的页面就必须需要打一大堆代码.
- public class MyServlet extends HttpServlet{
- public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
- response.setContentType("text/html");
- PrintWriter out = response.getWriter();
- out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
- out.println("<HTML>");
- out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
- out.println(" <BODY>");
- out.println("</IMG src='hackr.gif' alt='hackr.jp" width='240' height='84'/>
- out.println(" </BODY>");
- out.println("</HTML>");
- out.flush();
- out.close()
- }
- }
所以,Java官方推出了一个新的解决方案来替代前一方案,Servlet的应用服务器必须实现JSP编译器,用来编译jsp文件,将其转换成Servlet文件.
同样采用Servlet来进行响应处理,只不过将处理结果存于JSP文件中然后让编译器编译成结果Servlet,结果Servlet来输出信息至HTTP响应报文主体中.
这样就将原本的Servlet职责(Controller,View)分给了Servlets(Controller)和JSPs(View)这两个模块.
JSP编译后的文件存于tomcat/work目录下.
新的解决方案就多出了许多新的问题,
①一个Servlet怎么做到调用一个JSP文件(跳转);
②JSP如何知晓自己需要显示什么数据;
③JSP文件规范;
Servlet跳转
Servlet跳转就是以上的两种方式进行,
方式一使用response.sendRedirect(url); //直接输入跳转的url
方式二使用request.getRequestDispatcher(url).forward(request,response);
//输入跳转url作为定位,把request,response填入后进行跳转
因为JSP文件本质上会被编译成Servlet,所以可以使用Servlet与jsp进行跳转,只要把url填写为XXX.jsp即可.
Servlet数据存取
jsp需要获取信息来进行显示,获取的信息必定来自转换交接的Servlet那.所以Jsp显示什么数据的问题在于它能获取什么数据,Servlet存储了什么数据.
Servlet域对象指的是Servlet用来存储对象的区域,jsp可以在这些区域中获取数据,Servlet有三大域对象.
域对象存储方法{域}.setAttribute("objectName",Object);
域对象获取对象方法{域}.getAttribute("objectName");
ServletContext
这个域对象是所有Servlet的共用存储域,你保存的对象所有的Servlet都可以获取,jsp也是一种Servlet,所以它也可以获取.
Request
请求域对象,这个请求域在谁手里,谁就可以获取.只要通过转发这个Request,那么Servlet就可拿到这个域里面的对象.
Session
当客户端进行第一次访问时,应用服务器会为你创建一个会话对象;
并为你发送一个sessionID,这个SessionID会作为Cookie保存于你的浏览器中,作为访问这个会话域的凭证;
你可以在Session域中存取对象(用户信息),直至Session被销毁(一般是超时销毁);
JSP文件规范
请注意,JSP文件规范很大成分参考引入博文,非本人原创.
Apache基金会发布的J2EE规范历史版本.
下面我们使用JSP2.3版本来看一下JSP的规范.
JSP页面是动静结合来展示HTML页面内容的,静就是静态文件内容(HTML,CSS,JavaScript),动则是获取操作数据的JSP页面方法.
关于JSP你需要掌握以下内容以满足开发
脚本元素
JSP指令
JSP动作标签
内置对象
表达式语言EL
JSTL标签库
因为JSP被编译成Servlet,上述的动态语言元素都会被编译后写入静态文件中,我们通过编译前后文件来学习这些内容.
脚本元素
<%! %> 声明:定义翻译后Servlet程序的 全局变量或全局方法.内部类
<%= %> 表达式 输出内容到浏览器 效果等同于 out.print
<% %> 脚本代码块,嵌入java运行代码 ---- 不翻译
<%-- --%>JSP注释,编译成Servlet后消失
- <%@ 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>Insert title here</title>
- </head>
- <body>
- <h1>JSP脚本元素</h1>
- <%!
- // JSP声明 定义成员变量、成员方法 、内部类
- public static void m(){}
- class A {}
- %>
- <!-- 表达式 等价于 会被翻译为 out.print -->
- <%="abcd" %>
- <%
- // JSP 脚本代码块,嵌入任何java代码
- String s = "abcdefg";
- s = s.toUpperCase();
- out.print(s);
- %>
- <%-- JSP注释 --%>
- </body>
- </html>
demo.jsp
- /*
- * Generated by the Jasper component of Apache Tomcat
- * Version: Apache Tomcat/7.0.42
- * Generated at: 2016-09-03 12:18:11 UTC
- * Note: The last modified time of this file was set to
- * the last modified time of the source file after
- * generation to assist with modification tracking.
- */
- package org.apache.jsp;
- import javax.servlet.*;
- import javax.servlet.http.*;
- import javax.servlet.jsp.*;
- public final class demo1_jsp extends org.apache.jasper.runtime.HttpJspBase
- implements org.apache.jasper.runtime.JspSourceDependent {
- // JSP声明 定义成员变量、成员方法 、内部类
- public static void m(){}
- class A {}
- private static final javax.servlet.jsp.JspFactory _jspxFactory =
- javax.servlet.jsp.JspFactory.getDefaultFactory();
- private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;
- private javax.el.ExpressionFactory _el_expressionfactory;
- private org.apache.tomcat.InstanceManager _jsp_instancemanager;
- public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
- return _jspx_dependants;
- }
- public void _jspInit() {
- _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
- _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
- }
- public void _jspDestroy() {
- }
- public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
- throws java.io.IOException, javax.servlet.ServletException {
- final javax.servlet.jsp.PageContext pageContext;
- javax.servlet.http.HttpSession session = null;
- final javax.servlet.ServletContext application;
- final javax.servlet.ServletConfig config;
- javax.servlet.jsp.JspWriter out = null;
- final java.lang.Object page = this;
- javax.servlet.jsp.JspWriter _jspx_out = null;
- javax.servlet.jsp.PageContext _jspx_page_context = null;
- try {
- response.setContentType("text/html; charset=UTF-8");
- pageContext = _jspxFactory.getPageContext(this, request, response,
- null, true, 8192, true);
- _jspx_page_context = pageContext;
- application = pageContext.getServletContext();
- config = pageContext.getServletConfig();
- session = pageContext.getSession();
- out = pageContext.getOut();
- _jspx_out = out;
- out.write("\r\n");
- out.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\r\n");
- out.write("<html>\r\n");
- out.write("<head>\r\n");
- out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\r\n");
- out.write("<title>Insert title here</title>\r\n");
- out.write("</head>\r\n");
- out.write("<body>\r\n");
- out.write("<h1>JSP脚本元素</h1>\r\n");
- out.write("\r\n");
- out.write("\r\n");
- out.write("<!-- 表达式 等价于 会被翻译为 out.print -->\r\n");
- out.print("abcd" );
- out.write("\r\n");
- out.write("\r\n");
- // JSP 脚本代码块,嵌入任何java代码
- String s = "abcdefg";
- s = s.toUpperCase();
- out.print(s);
- out.write("\r\n");
- out.write("</body>\r\n");
- out.write("</html>");
- } catch (java.lang.Throwable t) {
- if (!(t instanceof javax.servlet.jsp.SkipPageException)){
- out = _jspx_out;
- if (out != null && out.getBufferSize() != 0)
- try { out.clearBuffer(); } catch (java.io.IOException e) {}
- if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
- else throw new ServletException(t);
- }
- } finally {
- _jspxFactory.releasePageContext(_jspx_page_context);
- }
- }
- }
demo_jsp.java
JSP指令
语法:<%@ 指令名称 属性=值 属性=值 ... %>
page指令
page指令用来定义JSP文件的全局属性 <%@ page 属性=值 %>
在JSP页面中,只有import可以出现多次,其他属性都只能出现一次
1.language 只能为java
2.extends 表示JSP翻译后的Servlet所继承的父类,这个属性一般不设置,因为服务器内部默认使jsp继承HttpJspBase类;如果非要设置,继承类必须是Servlet实现类
3.session 定义JSP中是否可以直接使用Session隐含对象,默认为true
如果属性设置为true,在JSP翻译Servlet时,生成以下两句代码:
HttpSession session = null;
session = pageContext.getSession();
* 如果jsp中想使用HttpSession对象,使用session属性默认值true
4.import 完成 JSP翻译后 Servlet 的导包
jsp在翻译为Servlet时,默认导入三个包:
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
jre默认导入 java.lang
* 在jsp中如果使用类 不属于以上四个包,就需要导包
5.buffer和autoFlush 设置 out隐含对象属性
buffer 设置缓冲区大小
autoFlush 设置当缓冲区满后,自动刷新
6.isELIgnored 设置JSP是否执行EL表达式
isELIgnored="false" 不忽略---执行解析
isELIgnored="true" 忽略 ---- 不解析
* 一般就是默认值false
7.通过contentType和pageEncoding 设置 JSP页面编码
pageEncoding 是 JSP文件源代码在硬盘上编码集,如果设置支持中文的编码集,那么服务器就能正确读取jsp中的中文,并将翻译好的中文字符读取进内存(注意内存中保存的不是字节)
contentType 在Servlet生成HTML.传递给浏览器时采用编码
* Java内存中,是没有编码集这一说的,存的都是字符
* 这两个属性设置成支持中文的编码集即可,互相之间不打架的
pageEncoding和contentType区别:
8.通过errorPage和isErrorPage 控制 JSP页面发生错误时跳转
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- <%--发生错误,想让用户看到友好页面 error.jsp--%>
- <%@ page errorPage="/demo4/error.jsp" %>
- <!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>Insert title here</title>
- </head>
- <body>
- <!-- 制作错误 -->
- <%
- int d = 1/0;
- %>
- </body>
- </html>
testErrorPage.jsp
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- <%--当设置了当前页面是错误页面,则可以获得内置对象exception,从而获得错误信息 --%>
- <%@page isErrorPage="true" %>
- <!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>Insert title here</title>
- </head>
- <body>
- <!-- 错误友好信息页面 -->
- <h4>对不起,服务器正在升级,请稍后访问!</h4>
- <h5>错误原因:<%=exception.getMessage() %></h5>
- </body>
- </html>
testIsErrorPage.jsp
关于错误页面配置,开发中比较常用的是在web.xml中配置<error-page>,一次配置即可.
- <error-page>
- <error-code>500</error-code>
- <location>/demo5/500.jsp</location>
- </error-page>
- <error-page>
- <error-code>404</error-code>
- <location>/demo5/404.jsp</location>
- </error-page>
include指令
用来静态包含页面 ----- 将页面公共部分提取出来,通过include完成页面布局。
语法:<%@ include file="文件路径" %>
include包含的是目标页面的整个内容,所以被包含页面,不需要是一个完整HTML,只要编写HTML片段就可以了。
- <%@ 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>Insert title here</title>
- </head>
- <body>
- <!-- 主页面 -->
- <!-- 通过 include 包含 logo.jsp -->
- <%@ include file="/demo6/logo.jsp" %>
- <h1>主页面其它内容</h1>
- <%--包含页面必须存在的--%>
- <%@ include file="/demo6/footer.jsp" %>
- </body>
- </html>
index.jsp
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- <h1>这是系统LOGO</h1>
logo.jsp
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- <%
- String s = "computer@mail.ustc.edu.cn";
- %>
- <%=s %>
footer.jsp
taglib指令
用来在jsp页面引用标签库文件
* 定义标签作用为了简化 jsp页面开发
* 通过taglib 指令引入 jstl标签库,语法: <%@ taglib uri="" prefix="" %>
uri ---- 定义标签 唯一命名空间
prefixt ---- 命名空间前缀
引用jstl时,在导入的jstl.jar中 META-INF/c.tld
<short-name>c</short-name> -------- 就是prefix属性
<uri>http://java.sun.com/jsp/jstl/core</uri> ----- 就是uri属性
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- <%--通过 taglib 指令 引用jstl ,必须导入jstl 的 jar包--%>
- <%--在 javaee 5 libraries 存在 jstl-1.2.jar--%>
- <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
- <!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>Insert title here</title>
- </head>
- <body>
- <%
- request.setAttribute("a",10);
- %>
- <c:if test="${requestScope.a>8}">
- <h1>a的值 大于8</h1>
- </c:if>
- </body>
- </html>
demo.jsp
JSP动作标签
JSP标签也称之为Jsp Action (JSP动作) 元素,它用于在Jsp页面中提供业务逻辑功能,避免在JSP页面中直接编写java代码,造成jsp页面难以维护。
注意,这些标签是默认存在的,不需要引入Jar包。
<jsp:include>
效果等价于request.getRequestDispatcher().include,原理是动态包含,区别于<%@ include file="文件路径" %>的静态包含
<jsp:forward>
<jsp:forward page="/demo11/b.jsp"></jsp:forward> 等价于 request.getRequestDispatcher("/demo11/b.jsp").forward(request,response);
- <%@ 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>Insert title here</title>
- </head>
- <body>
- <h1>Hello A</h1>
- <%
- // 看不到Hello A,因为在跳转之前,会清空response 缓冲区
- // request.getRequestDispatcher("/demo11/b.jsp").forward(request,response);
- %>
- <%
- request.setAttribute("name", "lichunchun");
- %>
- <jsp:forward page="/demo11/b.jsp">
- <jsp:param value="ustc" name="school"/>
- </jsp:forward>
- </body>
- </html>
demo.jsp
注:<jsp:forward>之后的代码不会被执行
<jsp:param >
绑定在<jsp:forward>中可以用来传递参数.
<jsp:forward page="/demo11/b.jsp">
<jsp:param value="ustc" name="school"/>
</jsp:forward>
<jsp:useBean>
<jsp:useBean>标签用来在jsp页面中创建一个Bean实例
<jsp:setProperty>
设置bean的属性
<jsp:getProperty>
获取bean的属性
- <%@ page language="java" pageEncoding="gb2312"%>
- <jsp:useBean id="user" scope="page" class="com.jsp.test.TestBean"/>
- <jsp:setProperty name="user" property="*"/>
- 或者用以下,param可以不填写,其中param对应的是提交页面的表单name
- <jsp:setProperty property="userName" name="user" param="userName"/>
- <jsp:setProperty property="password" name="user" param="password"/>
- <jsp:setProperty property="age" name="user" param="age"/>
- <html>
- <body>
- 注册成功:<br>
- <hr>
- 使用Bean的属性方法<br>
- 用户名: <%=user.getUserName()%><br>
- 密码: <%=user.getPassword()%><br>
- 年龄: <%=user.getAge()%><br>
- <hr>
- 使用getProperty<br>
- 用户名:<jsp:getProperty name="user" property="userName"/><br>
- 密码: <jsp:getProperty name="user" property="password"/><br>
- 年龄: <jsp:getProperty name="user" property="age"/>
- 客户端名称:<%=request.getRemoteAddr() %>
- </body>
- </html>
demo.jsp
内置对象
JSP编译为Servlet代码时,有些对象是默认已经创建好的,这类对象可以直接在jsp中使用,称之为九大内置对象.
page对象
page 代表当前jsp生成的Servlet对象
* page 是 Object类型,只能使用Object中方法 ---- 这个对象在开发中不建议使用
* 可以将page强制转换成HttpServlet对象
<%
HttpServlet httpServlet = (HttpServlet)page;
out.print(httpServlet.getServletContext().getRealPath("/"));
%>
JSP四种数据域对象
前面我们提到过,Servlet将数据存储于Request,ServletContext,Session三种域.
JSP在以上基础扩充了page对象,共有四种域对象(request,application,session,page),其中application是ServletContext的实现.
* page数据范围存放数据,只在当前jsp内有效
* 向page 范围保存数据,必须通过 pageContext对象 setAttribute方法
*pageContext还可以获取其他八个隐式对象.
out对象
out 功能向浏览器输出信息,是JspWriter类型,内部使用PrintWriter实现,拥有独立缓冲区。
out创建:out对象通过pageContext对象获得,而在创建pageContext对象时,需指定out缓冲区大小以及是否自动flush
* 通过 page指令 buffer autoFlush 设置out 缓存区大小 以及是否自动 flush,默认的缓冲区是8kb.
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- <%@ page isErrorPage="true" %>
- <%--通过 buffer和autoFlush 设置out 对象缓冲区--%>
- <%--<%@page buffer="1kb" autoFlush="false" %>--%>
- <!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>Insert title here</title>
- </head>
- <body>
- <h1>JSP 九个内置对象</h1>
- <%
- // 非要使用page对象
- HttpServlet httpServlet = (HttpServlet)page;
- out.print(httpServlet.getServletContext().getRealPath("/"));
- %>
- <hr/>
- <%
- // 向四种数据范围保存数据
- request.setAttribute("name","request");
- session.setAttribute("name","session");
- application.setAttribute("name","application");
- // 向page 范围保存数据,必须通过 pageContext对象
- pageContext.setAttribute("name","page");
- %>
- <%=request.getAttribute("name") %>
- <%=session.getAttribute("name") %>
- <%=application.getAttribute("name") %>
- <%=pageContext.getAttribute("name") %>
- <%
- // 想在四个数据范围查询 指定名称数据
- // 顺序按照 page -- request -- session -- application
- Object value = pageContext.findAttribute("name");
- %>
- <h3>查找name属性 :<%=value %></h3>
- <h1>通过EL 取得数据</h1>
- ${sessionScope.name }
- <!-- 如果直接写name 默认会调用 pageContext.findAttribute -->
- ${name }
- </body>
- </html>
demo.jsp
观察JSP编译的Servlet文件可以查看这些隐式对象.
exception对象
exception对象是java.lang.Trowable类的实例 (使用前需要在jsp页面设置page指令 isErrorPage=“true”)
exception对象用来处理JSP文件在执行时所有发生的错误和异常
exception对象可以和page指令一起使用,通过指定某一个页面为错误处理页面,对错误进行处理
<%@ page isErrorPage="true"%>的页面内使用。(最好还是用第二种配置web.xml的方式)
表达式语言EL
EL语言属于小团队开发,后来在Servlet2.4之后被并入了官方规范之中,目的是为了简化JSP代码开发.
主要功能:
获取JSP四个范围中保存的数据
${pageScope.属性名称}
${requestScope.属性名称}
${sessionScope.属性名称}
${applicationScope.属性名}
如果查找属性不存在,返回是一个 "" 空串,而不是null
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8" isELIgnored="false"%>
- <!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>Insert title here</title>
- </head>
- <body>
- <!-- 通过el 获得四个数据范围 数据 page request session application-->
- <%
- pageContext.setAttribute("city","合肥");
- request.setAttribute("name","李春春");
- session.setAttribute("school","中国科学技术大学");
- application.setAttribute("pnum",100);
- %>
- ${pageScope.city }
- ${requestScope.name }
- ${sessionScope.school }
- ${applicationScope.pnum }
- <h1>省略指定范围, 默认调用pageContext.findAttribute() 在四个范围依次查找</h1>
- ${name }
- ${city }
- <h1>EL找不到数据返回""空串、传统表达式方式找不到数据返回null</h1>
- <h3>abc: <%=request.getAttribute("abc") %></h3>
- <h3>abc: ${abc }</h3>
- </body>
- </html>
demo.jsp
获取JavaBean属性,数组,Collection,Map等数据集合
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- <%@page import="ustc.lichunchun.domain.Person"%>
- <%@page import="ustc.lichunchun.domain.City"%>
- <!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>Insert title here</title>
- </head>
- <body>
- <!-- 通过EL 获得 存放在四个范围内的 java对象类型 -->
- <%
- Person person = new Person();
- person.setName("李春春");
- person.setAge(24);
- City city = new City();
- city.setName("合肥");
- person.setCity(city);
- pageContext.setAttribute("person", person);
- %>
- ${pageScope.person.name }
- <!-- 上面写法等价于 pageContext.getAttribute("person").getName() -->
- ${pageScope.person.age }
- ${pageScope.person["age"] }
- ${pageScope["person"]["age"] }
- <!-- 获得person的city对象名称 -->
- ${pageScope.person.city.name }
- <!-- pageContext.getAttribute("person").getCity().getName() -->
- ${pageScope["person"]["city"]["name"] }
- </body>
- </html>
getFromJavaBean.jsp
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- <%@page import="java.util.List"%>
- <%@page import="java.util.ArrayList"%>
- <%@page import="java.util.Map"%>
- <%@page import="java.util.HashMap"%>
- <!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>Insert title here</title>
- </head>
- <body>
- <!-- 通过EL 取得 List 或者 Map中数据 -->
- <%
- List list = new ArrayList();
- list.add("abc");
- list.add("bcd");
- list.add("efg");
- // 将list 保存page范围
- pageContext.setAttribute("list",list);
- %>
- ${pageScope.list }
- 取得list的第二个元素 :${pageScope.list[1] }<br/>
- <%
- Map map = new HashMap();
- map.put("aaa","111");
- map.put("bbb","222");
- pageContext.setAttribute("map",map);
- %>
- 取得 map 中 bbb对应 value : ${pageScope.map.bbb }、${pageScope.map["bbb"] }<br/>
- </body>
- </html>
getFromList.jsp
上述代码获取数组,List,Map时,可以使用.或者[]获取
. 和 [ ] 有什么区别 ?
答案:. 和 [ ] 都可以用来取得EL 属性值,.可以实现的功能[ ] 也都可以!
例如: ${pageScope.user.name} 也可以写为 ${pageScope.user["name"]}
[ ] 可以使用特殊标识信息,但是. 不可以
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- <%@page import="java.util.List"%>
- <%@page import="java.util.ArrayList"%>
- <%@page import="java.util.Map"%>
- <%@page import="java.util.HashMap"%>
- <!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>Insert title here</title>
- </head>
- <body>
- <!-- 通过EL 取得 List 或者 Map中数据 -->
- <%
- List list = new ArrayList();
- list.add("abc");
- list.add("bcd");
- list.add("efg");
- // 将list 保存page范围
- pageContext.setAttribute("list",list);
- %>
- ${pageScope.list }
- 取得list的第二个元素 :${pageScope.list[1] }<br/>
- <%
- Map map = new HashMap();
- map.put("aaa","111");
- map.put("bbb","222");
- pageContext.setAttribute("map",map);
- %>
- 取得 map 中 bbb对应 value : ${pageScope.map.bbb }、${pageScope.map["bbb"] }<br/>
- <h1>. 和 [] 区别</h1>
- <%
- pageContext.setAttribute("0","itcast");
- pageContext.setAttribute("aa.bb","特殊标识信息");
- %>
- 特殊字符0 属性值:${pageScope["0"] } <br/>
- 特殊字符 aa.bb 属性值 :${pageScope["aa.bb"] } <br/>
- <%
- String ds = "aa.bb";
- pageContext.setAttribute("s",ds);
- %>
- <!-- 在使用[] 进行属性取值时,要加"" , 若不加"" 则认为是一个变量 -->
- 特殊字符 aa.bb 属性值 :${pageScope[s] }<br/><!-- 特殊标识信息 -->
- 特殊字符 aa.bb 属性值 :${pageScope["s"] }<!-- aa.bb -->
- <!-- 利用el表达式获取web应用的名称 -->
- <a href="${pageContext.request.contextPath }/demo1.jsp">点我</a>
- </body>
- </html>
difference.jsp
算术,比较,逻辑运算
在EL 执行运算时,运算语句必须写入 ${ }中
* 在EL 获得属性值 执行算术运算,自动类型转换 ---- 执行算术运算时,进行运算参数,必须都是数字
${"a"+"b"} ---- 发生数字格式化错误
empty运算符
1) 判断一个属性是否存在 , 通常empty运算符都是结合c:if 一起使用
2) 使用empty 判断List 或者 Map是否为空 (size==0)
二元表达式:${user!=null?user.name:""} ----- 三元运算符
不能使用保留字存储属性,保留字有特殊意义
EL表达式保留关键字:
- <%@page import="java.util.HashMap"%>
- <%@page import="java.util.ArrayList"%>
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
- <!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>Insert title here</title>
- </head>
- <body>
- <h2>EL 执行 运算</h2>
- <%
- pageContext.setAttribute("a", "10");
- pageContext.setAttribute("b", "20");
- pageContext.setAttribute("10", "30");
- %>
- ${a+b }<!-- 30 -->
- <%--经典错误 :${"a"+"b" }--%>
- ${pageScope.a }<!-- 10 -->
- ${pageScope["a"] }<!-- 10 -->
- ${pageScope[a] }<!-- 30 -->
- ${a }<!-- 10 -->
- ${"a" }<!-- a -->
- <h2>empty运算符</h2>
- ${empty name }<!-- 如果四个数据范围都没有name属性 返回true -->
- <c:if test="${empty name }">
- <h3>根本不存在 name数据</h3>
- </c:if>
- <!-- 判断list 获得 map是否为空 -->
- <%
- pageContext.setAttribute("list", new ArrayList());
- pageContext.setAttribute("map", new HashMap());
- %>
- ${empty list }
- ${empty map }
- <h2>二元表达式</h2>
- ${(empty map)?"map中没有任何元素":"map不为空" }
- <%
- // 不能使用保留字 存储属性,保留字有特殊意义
- pageContext.setAttribute("empty","111");
- %>
- <%--${pageContext["empty"] }--%>
- </body>
- </html>
demo.jsp
内置11个对象(web开发常用对象)
JSTL标签库
项目开发中,JSP开发基本都会约定引入JSTL标签库(Java Standard Tag Liberary),统一规范,简化代码开发.
下载jstl.jar和standard.jar,通过taglib指令引入jstl标签库对应的uri,也可以在web.xml中直接进行配置
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app version="2.4"
- xmlns="http://java.sun.com/xml/ns/j2ee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
- http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
- <jsp-config>
- <taglib>
- <taglib-uri>http://java.sun.com/jstl/fmt</taglib-uri>
- <taglib-location>/WEB-INF/fmt.tld</taglib-location>
- </taglib>
- <taglib>
- <taglib-uri>http://java.sun.com/jstl/fmt-rt</taglib-uri>
- <taglib-location>/WEB-INF/fmt-rt.tld</taglib-location>
- </taglib>
- <taglib>
- <taglib-uri>http://java.sun.com/jstl/core</taglib-uri>
- <taglib-location>/WEB-INF/c.tld</taglib-location>
- </taglib>
- <taglib>
- <taglib-uri>http://java.sun.com/jstl/core-rt</taglib-uri>
- <taglib-location>/WEB-INF/c-rt.tld</taglib-location>
- </taglib>
- <taglib>
- <taglib-uri>http://java.sun.com/jstl/sql</taglib-uri>
- <taglib-location>/WEB-INF/sql.tld</taglib-location>
- </taglib>
- <taglib>
- <taglib-uri>http://java.sun.com/jstl/sql-rt</taglib-uri>
- <taglib-location>/WEB-INF/sql-rt.tld</taglib-location>
- </taglib>
- <taglib>
- <taglib-uri>http://java.sun.com/jstl/x</taglib-uri>
- <taglib-location>/WEB-INF/x.tld</taglib-location>
- </taglib>
- <taglib>
- <taglib-uri>http://java.sun.com/jstl/x-rt</taglib-uri>
- <taglib-location>/WEB-INF/x-rt.tld</taglib-location>
- </taglib>
- </jsp-config>
- </web-app>
web.xml
JSTL由五种主要标签组成:
具体的语法参考
过滤器,监听器
过滤器可以对请求和响应做出拦截操作.
配置多个filter:
1.继承filter类,实现init,doFilter,destroy三个类方法.
- //导入必需的 java 库
- import javax.servlet.*;
- import java.util.*;
- //实现 Filter 类
- public class LogFilter implements Filter {
- public void init(FilterConfig config) throws ServletException {
- // 获取初始化参数
- String site = config.getInitParameter("Site");
- // 输出初始化参数
- System.out.println("网站名称: " + site);
- }
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException {
- // 输出站点名称
- System.out.println("站点网址:http://www.runoob.com");
- // 把请求传回过滤链
- chain.doFilter(request,response);
- }
- public void destroy( ){
- /* 在 Filter 实例被 Web 容器从服务移除之前调用 */
- }
- }
logFilter.java
2.在web.xml进行配置
- <filter>
- <filter-name>LogFilter</filter-name>
- <filter-class>com.runoob.test.LogFilter</filter-class>
- <init-param>
- <param-name>test-param</param-name>
- <param-value>Initialization Paramter</param-value>
- </init-param>
- </filter>
- <filter>
- <filter-name>AuthenFilter</filter-name>
- <filter-class>com.runoob.test.AuthenFilter</filter-class>
- <init-param>
- <param-name>test-param</param-name>
- <param-value>Initialization Paramter</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>LogFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- <filter-mapping>
- <filter-name>AuthenFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
web.xml
监听器顾名思义,用来观察对象的状态.在Servlet规范中提供了8个监听器接口.
创建、销毁监听器3个
ServletContextListener:监听ServletContext的创建和销毁的监听器
HttpSessionListener:监听HttpSession的创建和销毁的监听器
ServletRequestListener:监听ServletRequest的创建和销毁的监听器
属性变化监听器3个
ServletContextAttributeListener:监听放到应用范围中的数据变化(新添加、修改的、删除的)
HttpSessionAttributeListener:(统计登录用户列表)
ServletRequestAttributeListener
感知型监听器2个
HttpSessionBindingListener:谁实现这个接口,就能感知自己何时被HttpSession绑定和解绑了
HttpSessionActivationListener:谁实现这个接口,就能感知自己何时随着HttpSession对象钝化和激活
web.xml中配置监听器
- <listener>
- <listener-class>
- com.journaldev.listener.AppContextListener
- </listener-class>
- </listener>
文件上传下载
上传
注意,这里代码是搬运苍狼大神的博文,用于模仿学习.
文件的上传下载是使用流来进行传输,java web一般使用的是apache的file-upload组件.
1.引入commons-fileupload.jar和commons-io.jar
2.表单中需要<input>的type标为file,
enctype标为multipart/form-data,
method标为post
3. 编写Servlet
- package me.gacl.web.controller;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- 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.disk.DiskFileItemFactory;
- import org.apache.commons.fileupload.servlet.ServletFileUpload;
- public class UploadHandleServlet extends HttpServlet {
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- //得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不允许外界直接访问,保证上传文件的安全
- String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
- File file = new File(savePath);
- //判断上传文件的保存目录是否存在
- if (!file.exists() && !file.isDirectory()) {
- System.out.println(savePath+"目录不存在,需要创建");
- //创建目录
- file.mkdir();
- }
- //消息提示
- String message = "";
- try{
- //使用Apache文件上传组件处理文件上传步骤:
- //1、创建一个DiskFileItemFactory工厂
- DiskFileItemFactory factory = new DiskFileItemFactory();
- //2、创建一个文件上传解析器
- ServletFileUpload upload = new ServletFileUpload(factory);
- //解决上传文件名的中文乱码
- upload.setHeaderEncoding("UTF-8");
- //3、判断提交上来的数据是否是上传表单的数据
- if(!ServletFileUpload.isMultipartContent(request)){
- //按照传统方式获取数据
- return;
- }
- //4、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem对应一个Form表单的输入项
- List<FileItem> list = upload.parseRequest(request);
- for(FileItem item : list){
- //如果fileitem中封装的是普通输入项的数据
- if(item.isFormField()){
- String name = item.getFieldName();
- //解决普通输入项的数据的中文乱码问题
- String value = item.getString("UTF-8");
- //value = new String(value.getBytes("iso8859-1"),"UTF-8");
- System.out.println(name + "=" + value);
- }else{//如果fileitem中封装的是上传文件
- //得到上传的文件名称,
- String filename = item.getName();
- System.out.println(filename);
- if(filename==null || filename.trim().equals("")){
- continue;
- }
- //注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如: c:\a\b\1.txt,而有些只是单纯的文件名,如:1.txt
- //处理获取到的上传文件的文件名的路径部分,只保留文件名部分
- filename = filename.substring(filename.lastIndexOf("\\")+1);
- //获取item中的上传文件的输入流
- InputStream in = item.getInputStream();
- //创建一个文件输出流
- FileOutputStream out = new FileOutputStream(savePath + "\\" + filename);
- //创建一个缓冲区
- byte buffer[] = new byte[1024];
- //判断输入流中的数据是否已经读完的标识
- int len = 0;
- //循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据
- while((len=in.read(buffer))>0){
- //使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\\" + filename)当中
- out.write(buffer, 0, len);
- }
- //关闭输入流
- in.close();
- //关闭输出流
- out.close();
- //删除处理文件上传时生成的临时文件
- item.delete();
- message = "文件上传成功!";
- }
- }
- }catch (Exception e) {
- message= "文件上传失败!";
- e.printStackTrace();
- }
- request.setAttribute("message",message);
- request.getRequestDispatcher("/message.jsp").forward(request, response);
- }
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- doGet(request, response);
- }
- }
4.web.xml注册Servlet
- <servlet>
- <servlet-name>UploadHandleServlet</servlet-name>
- <servlet-class>me.gacl.web.controller.UploadHandleServlet</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>UploadHandleServlet</servlet-name>
- <url-pattern>/servlet/UploadHandleServlet</url-pattern>
- </servlet-mapping>
5.改进
1、为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于WEB-INF目录下。
2、为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名。
3、为防止一个目录下面出现太多文件,要使用hash算法打散存储。
4、要限制上传文件的最大值。
5、要限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。
- package me.gacl.web.controller;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.List;
- import java.util.UUID;
- 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.FileUploadBase;
- import org.apache.commons.fileupload.ProgressListener;
- import org.apache.commons.fileupload.disk.DiskFileItemFactory;
- import org.apache.commons.fileupload.servlet.ServletFileUpload;
- /**
- * @ClassName: UploadHandleServlet
- * @Description: TODO(这里用一句话描述这个类的作用)
- * @author: 孤傲苍狼
- * @date: 2015-1-3 下午11:35:50
- *
- */
- public class UploadHandleServlet extends HttpServlet {
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- //得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不允许外界直接访问,保证上传文件的安全
- String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
- //上传时生成的临时文件保存目录
- String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp");
- File tmpFile = new File(tempPath);
- if (!tmpFile.exists()) {
- //创建临时目录
- tmpFile.mkdir();
- }
- //消息提示
- String message = "";
- try{
- //使用Apache文件上传组件处理文件上传步骤:
- //1、创建一个DiskFileItemFactory工厂
- DiskFileItemFactory factory = new DiskFileItemFactory();
- //设置工厂的缓冲区的大小,当上传的文件大小超过缓冲区的大小时,就会生成一个临时文件存放到指定的临时目录当中。
- factory.setSizeThreshold(1024*100);//设置缓冲区的大小为100KB,如果不指定,那么缓冲区的大小默认是10KB
- //设置上传时生成的临时文件的保存目录
- factory.setRepository(tmpFile);
- //2、创建一个文件上传解析器
- ServletFileUpload upload = new ServletFileUpload(factory);
- //监听文件上传进度
- upload.setProgressListener(new ProgressListener(){
- public void update(long pBytesRead, long pContentLength, int arg2) {
- System.out.println("文件大小为:" + pContentLength + ",当前已处理:" + pBytesRead);
- /**
- * 文件大小为:14608,当前已处理:4096
- 文件大小为:14608,当前已处理:7367
- 文件大小为:14608,当前已处理:11419
- 文件大小为:14608,当前已处理:14608
- */
- }
- });
- //解决上传文件名的中文乱码
- upload.setHeaderEncoding("UTF-8");
- //3、判断提交上来的数据是否是上传表单的数据
- if(!ServletFileUpload.isMultipartContent(request)){
- //按照传统方式获取数据
- return;
- }
- //设置上传单个文件的大小的最大值,目前是设置为1024*1024字节,也就是1MB
- upload.setFileSizeMax(1024*1024);
- //设置上传文件总量的最大值,最大值=同时上传的多个文件的大小的最大值的和,目前设置为10MB
- upload.setSizeMax(1024*1024*10);
- //4、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem对应一个Form表单的输入项
- List<FileItem> list = upload.parseRequest(request);
- for(FileItem item : list){
- //如果fileitem中封装的是普通输入项的数据
- if(item.isFormField()){
- String name = item.getFieldName();
- //解决普通输入项的数据的中文乱码问题
- String value = item.getString("UTF-8");
- //value = new String(value.getBytes("iso8859-1"),"UTF-8");
- System.out.println(name + "=" + value);
- }else{//如果fileitem中封装的是上传文件
- //得到上传的文件名称,
- String filename = item.getName();
- System.out.println(filename);
- if(filename==null || filename.trim().equals("")){
- continue;
- }
- //注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如: c:\a\b\1.txt,而有些只是单纯的文件名,如:1.txt
- //处理获取到的上传文件的文件名的路径部分,只保留文件名部分
- filename = filename.substring(filename.lastIndexOf("\\")+1);
- //得到上传文件的扩展名
- String fileExtName = filename.substring(filename.lastIndexOf(".")+1);
- //如果需要限制上传的文件类型,那么可以通过文件的扩展名来判断上传的文件类型是否合法
- System.out.println("上传的文件的扩展名是:"+fileExtName);
- //获取item中的上传文件的输入流
- InputStream in = item.getInputStream();
- //得到文件保存的名称
- String saveFilename = makeFileName(filename);
- //得到文件的保存目录
- String realSavePath = makePath(saveFilename, savePath);
- //创建一个文件输出流
- FileOutputStream out = new FileOutputStream(realSavePath + "\\" + saveFilename);
- //创建一个缓冲区
- byte buffer[] = new byte[1024];
- //判断输入流中的数据是否已经读完的标识
- int len = 0;
- //循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据
- while((len=in.read(buffer))>0){
- //使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\\" + filename)当中
- out.write(buffer, 0, len);
- }
- //关闭输入流
- in.close();
- //关闭输出流
- out.close();
- //删除处理文件上传时生成的临时文件
- //item.delete();
- message = "文件上传成功!";
- }
- }
- }catch (FileUploadBase.FileSizeLimitExceededException e) {
- e.printStackTrace();
- request.setAttribute("message", "单个文件超出最大值!!!");
- request.getRequestDispatcher("/message.jsp").forward(request, response);
- return;
- }catch (FileUploadBase.SizeLimitExceededException e) {
- e.printStackTrace();
- request.setAttribute("message", "上传文件的总的大小超出限制的最大值!!!");
- request.getRequestDispatcher("/message.jsp").forward(request, response);
- return;
- }catch (Exception e) {
- message= "文件上传失败!";
- e.printStackTrace();
- }
- request.setAttribute("message",message);
- request.getRequestDispatcher("/message.jsp").forward(request, response);
- }
- /**
- * @Method: makeFileName
- * @Description: 生成上传文件的文件名,文件名以:uuid+"_"+文件的原始名称
- * @Anthor:孤傲苍狼
- * @param filename 文件的原始名称
- * @return uuid+"_"+文件的原始名称
- */
- private String makeFileName(String filename){ //2.jpg
- //为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名
- return UUID.randomUUID().toString() + "_" + filename;
- }
- /**
- * 为防止一个目录下面出现太多文件,要使用hash算法打散存储
- * @Method: makePath
- * @Description:
- * @Anthor:孤傲苍狼
- *
- * @param filename 文件名,要根据文件名生成存储目录
- * @param savePath 文件存储路径
- * @return 新的存储目录
- */
- private String makePath(String filename,String savePath){
- //得到文件名的hashCode的值,得到的就是filename这个字符串对象在内存中的地址
- int hashcode = filename.hashCode();
- int dir1 = hashcode&0xf; //0--15
- int dir2 = (hashcode&0xf0)>>4; //0-15
- //构造新的保存目录
- String dir = savePath + "\\" + dir1 + "\\" + dir2; //upload\2\3 upload\3\5
- //File既可以代表文件也可以代表目录
- File file = new File(dir);
- //如果目录不存在
- if(!file.exists()){
- //创建目录
- file.mkdirs();
- }
- return dir;
- }
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- doGet(request, response);
- }
- }
下载
1.下载文件页面
- <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
- <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
- <!DOCTYPE HTML>
- <html>
- <head>
- <title>下载文件显示页面</title>
- </head>
- <body>
- <!-- 遍历Map集合 -->
- <c:forEach var="me" items="${fileNameMap}">
- <c:url value="/servlet/DownLoadServlet" var="downurl">
- <c:param name="filename" value="${me.key}"></c:param>
- </c:url>
- ${me.value}<a href="${downurl}">下载</a>
- <br/>
- </c:forEach>
- </body>
- </html>
2.执行下载操作的Servlet
- package me.gacl.web.controller;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.io.OutputStream;
- import java.net.URLEncoder;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- public class DownLoadServlet extends HttpServlet {
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- //得到要下载的文件名
- String fileName = request.getParameter("filename"); //23239283-92489-阿凡达.avi
- fileName = new String(fileName.getBytes("iso8859-1"),"UTF-8");
- //上传的文件都是保存在/WEB-INF/upload目录下的子目录当中
- String fileSaveRootPath=this.getServletContext().getRealPath("/WEB-INF/upload");
- //通过文件名找出文件的所在目录
- String path = findFileSavePathByFileName(fileName,fileSaveRootPath);
- //得到要下载的文件
- File file = new File(path + "\\" + fileName);
- //如果文件不存在
- if(!file.exists()){
- request.setAttribute("message", "您要下载的资源已被删除!!");
- request.getRequestDispatcher("/message.jsp").forward(request, response);
- return;
- }
- //处理文件名
- String realname = fileName.substring(fileName.indexOf("_")+1);
- //设置响应头,控制浏览器下载该文件
- response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(realname, "UTF-8"));
- //读取要下载的文件,保存到文件输入流
- FileInputStream in = new FileInputStream(path + "\\" + fileName);
- //创建输出流
- OutputStream out = response.getOutputStream();
- //创建缓冲区
- byte buffer[] = new byte[1024];
- int len = 0;
- //循环将输入流中的内容读取到缓冲区当中
- while((len=in.read(buffer))>0){
- //输出缓冲区的内容到浏览器,实现文件下载
- out.write(buffer, 0, len);
- }
- //关闭文件输入流
- in.close();
- //关闭输出流
- out.close();
- }
- /**
- * @Method: findFileSavePathByFileName
- * @Description: 通过文件名和存储上传文件根目录找出要下载的文件的所在路径
- * @Anthor:孤傲苍狼
- * @param filename 要下载的文件名
- * @param saveRootPath 上传文件保存的根目录,也就是/WEB-INF/upload目录
- * @return 要下载的文件的存储目录
- */
- public String findFileSavePathByFileName(String filename,String saveRootPath){
- int hashcode = filename.hashCode();
- int dir1 = hashcode&0xf; //0--15
- int dir2 = (hashcode&0xf0)>>4; //0-15
- String dir = saveRootPath + "\\" + dir1 + "\\" + dir2; //upload\2\3 upload\3\5
- File file = new File(dir);
- if(!file.exists()){
- //创建目录
- file.mkdirs();
- }
- return dir;
- }
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- doGet(request, response);
- }
- }
3.web.xml配置
- <servlet>
- <servlet-name>DownLoadServlet</servlet-name>
- <servlet-class>me.gacl.web.controller.DownLoadServlet</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>DownLoadServlet</servlet-name>
- <url-pattern>/servlet/DownLoadServlet</url-pattern>
- </servlet-mapping>
编程语言 : Java的动态Web解决方案泛谈的更多相关文章
- JavaWeb学习笔记——开发动态WEB资源(一)Java程序向浏览器输出数据
开发一个动态web资源,即开发一个Java程序向浏览器输出数据,需要完成以下2个步骤: 1.编写一个Java类,实现Servlet接口 开发一个动态web资源必须实现javax.servlet.Ser ...
- Eclipse新建动态web工程项目出现红叉解决方案
问题描述:之前新建动态web工程一直没有问题,今天新建一个项目后项目名称上突然出现小红叉,子目录文件没有红叉. 解决过程:一开始想到的就是编译器的level设置,调整了一下,仍然没有解决. 然后在标记 ...
- Java Servlet与Web容器之间的关系
自从计算机软件开发进入网络时代,就开始涉及到通讯问题.在客户/服务器(也叫C/S应用)时期,每个软件都有自己的客户端和服务器端软件.并且客户端和服务器端之间的通讯协议差别也很大.后来随着互联网的发展, ...
- 动态 Web Server 技术发展历程
动态 Web Server 技术发展历程 开始接触 Java Web 方面的技术,此篇文章是以介绍 Web server 相关技术的演变为主来作为了解 Java servlet 的技术背景,目的是更好 ...
- java定时调度器解决方案分类及特性介绍
什么是定时调度器? 我们知道程序的运行要么是由事件触发的,而这种事件的触发源头往往是用户通过ui交互操作层层传递过来的:但是我们知道还有另外一种由机器系统时间触发的程序运行场景.大家想想是否遇到或者听 ...
- Java中动态规则的实现方式
背景 业务系统在应用过程中,有时候要处理“经常变化”的部分,这部分需求可能是“业务规则”,也可能是“不同的数据处理逻辑”,这部分动态规则的问题,往往需要可配置,并对性能和实时性有一定要求. Java不 ...
- 使用Java创建RESTful Web Service
REST是REpresentational State Transfer的缩写(一般中文翻译为表述性状态转移).2000年Roy Fielding博士在他的博士论文“Architectural Sty ...
- java中dynamic web project与web project 的区别 [转]
原帖地址:http://blog.sina.com.cn/s/blog_46726d2401013jlk.html 文章框架: 1.Dynamic Web Project 概念 2.eclipse ...
- 使用Java创建RESTful Web Service(转)
REST是REpresentational State Transfer的缩写(一般中文翻译为表述性状态转移).2000年Roy Fielding博士在他的博士论文“Architectural Sty ...
随机推荐
- Windows开启telnet服务 + 连接失败处理
一.控制面板中安装Telnet相关组件 单击"开始"菜单,单击"控制面板" 在控制面板中单击打开"程序和功能"项目 在左侧的蓝色 ...
- Elasticsearch-sql 用SQL查询Elasticsearch
Elasticsearch的查询语言(DSL)真是不好写,偏偏查询的功能千奇百怪,filter/query/match/agg/geo各种各样,不管你是通过封装JSON还是通过python/java的 ...
- Pyhton编程(二)之变量、用户输入及条件语句
一:变量 变量定义的规则 1)只能由数字.字母.下划线组成(不能以数字开头) 2)不能使用关键字作为变量名 ['and', 'as', 'assert', 'break', 'class', 'con ...
- exp3.1实现顺序栈的各种操作
#include<iostream>using namespace std;#include<malloc.h>typedef char Elem;typedef struct ...
- MyServer
//一.设置一个8089端口的本地IP服务器 1 package myserver; import java.io.IOException; import java.net.ServerSocket; ...
- 解决 Win10 UWP 无法使用 ss 连接
一旦使用了 ss, 那么很多应用就无法连接网络. 本文提供一个方法可以简单使用ss提供的代理. 多谢 wtwsgs 提供方法:http://blog.csdn.net/wtwsgs/article/d ...
- 测试中出现ERROR StatusLogger No log4j2 configuration file
概述 在hibernate框架搭建完成用log4j2进行测试时,总是出现ERROR StatusLogger No log4j2 configuration file found. Using def ...
- 第一篇bolg
仅以此篇谨记自己,以后加油
- MySQL比like语句更高效的写法locate position instr find_in_set
使用内部函数instr,可代替传统的like方式查询,并且速度更快. instr函数,第一个参数是字段,第二个参数是要查询的串,返回串的位置,第一个是1,如果没找到就是0. 例如, select na ...
- 【NOIP模拟】的士碰撞(二分答案)
Description