文章概述

最近发现很久前一股脑地学习框架,发觉越发迷糊.知道了框架只是暂时的,重点是基础的技术.该文大篇幅回顾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.通过errorPageisErrorPage 控制 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解决方案泛谈的更多相关文章

  1. JavaWeb学习笔记——开发动态WEB资源(一)Java程序向浏览器输出数据

    开发一个动态web资源,即开发一个Java程序向浏览器输出数据,需要完成以下2个步骤: 1.编写一个Java类,实现Servlet接口 开发一个动态web资源必须实现javax.servlet.Ser ...

  2. Eclipse新建动态web工程项目出现红叉解决方案

    问题描述:之前新建动态web工程一直没有问题,今天新建一个项目后项目名称上突然出现小红叉,子目录文件没有红叉. 解决过程:一开始想到的就是编译器的level设置,调整了一下,仍然没有解决. 然后在标记 ...

  3. Java Servlet与Web容器之间的关系

    自从计算机软件开发进入网络时代,就开始涉及到通讯问题.在客户/服务器(也叫C/S应用)时期,每个软件都有自己的客户端和服务器端软件.并且客户端和服务器端之间的通讯协议差别也很大.后来随着互联网的发展, ...

  4. 动态 Web Server 技术发展历程

    动态 Web Server 技术发展历程 开始接触 Java Web 方面的技术,此篇文章是以介绍 Web server 相关技术的演变为主来作为了解 Java servlet 的技术背景,目的是更好 ...

  5. java定时调度器解决方案分类及特性介绍

    什么是定时调度器? 我们知道程序的运行要么是由事件触发的,而这种事件的触发源头往往是用户通过ui交互操作层层传递过来的:但是我们知道还有另外一种由机器系统时间触发的程序运行场景.大家想想是否遇到或者听 ...

  6. Java中动态规则的实现方式

    背景 业务系统在应用过程中,有时候要处理“经常变化”的部分,这部分需求可能是“业务规则”,也可能是“不同的数据处理逻辑”,这部分动态规则的问题,往往需要可配置,并对性能和实时性有一定要求. Java不 ...

  7. 使用Java创建RESTful Web Service

    REST是REpresentational State Transfer的缩写(一般中文翻译为表述性状态转移).2000年Roy Fielding博士在他的博士论文“Architectural Sty ...

  8. java中dynamic web project与web project 的区别 [转]

    原帖地址:http://blog.sina.com.cn/s/blog_46726d2401013jlk.html 文章框架: 1.Dynamic  Web Project 概念 2.eclipse ...

  9. 使用Java创建RESTful Web Service(转)

    REST是REpresentational State Transfer的缩写(一般中文翻译为表述性状态转移).2000年Roy Fielding博士在他的博士论文“Architectural Sty ...

随机推荐

  1. 使用Gradle构建Android项目

    阅读目录 Gradle是什么? 环境需求 Gradle基本结构 任务task的执行 基本的构建定制 目录配置 签名配置 代码混淆设置 依赖配置 输出不同配置的应用 生成多个渠道包(以Umeng为例) ...

  2. 【Windows 10 应用开发】使用x:Bind标记动态获得计算结果

    UWP 在传统(WPF)的Binding标记上引入了 Bind 标记,Bind 基于编译阶段生成,因而具有较高的性能.但是,你得注意,这个性能上的优化是免去了运行阶段动态绑定的开销,这是不包括数据源的 ...

  3. 张高兴的 Xamarin.Android 学习笔记:(一)环境配置

    最近在自学 Xamarin 和 Android ,同时发现国内在做 Xamarin 的不多.我在自学中间遇到了很多问题,而且百度到的很多教程也有些过时,现在打算写点东西稍微总结下,顺便帮后人指指路了. ...

  4. WPF DataGridHyperlinkColumn

    为了点击链接,我们使用 <DataGrid x:Name="data" LoadingRow="load" ItemsSource="{Bind ...

  5. Android Studio中的Java控制台中出现乱码问题?

    今天在用Android studio 中敲代码时发现控制台出不了汉字,一打汉字全是乱码的.在此特供解决方案. 在Java的工程目录build.gradle下添加如下代码: 1.新版gradle tas ...

  6. Vue源码后记-钩子函数

    vue源码的马拉松跑完了,可以放松一下写点小东西,其实源码讲20节都讲不完,跳了好多地方. 本人技术有限,无法跟大神一样,模拟vue手把手搭建一个MVVM框架,然后再分析原理,只能以门外汉的姿态简单过 ...

  7. HTML的语法

    1,什么是HTML标记语言,他是表示网页信息的符号标记语言,特点包括: a,可以设置文本的格式,比如标题,文号,文本颜色,段落等待 b,可以简历列表 c,可以插入图像和媒体 d,可以建立表格 e,超连 ...

  8. 原生JS实现图片放大镜插件

      前  言 我们大家经常逛各种电商类的网站,商品的细节就需要用到放大镜,这个大家一定不陌生,今天我们就做一个图片放大镜的插件,来看看图片是如何被放大的…… 先看一下我们要是实现的最终效果是怎么样的  ...

  9. jQuery点击下拉菜单的展示与隐藏

    首先点击显示某个div,然后要求再次点击时消失,或者点击document的其他地方会隐藏掉这个层,涉及到冒泡的问题,阻止document冒泡到dom上.代码如下: var $el = $(" ...

  10. Linux系列教程(十四)——Linux用户和用户组管理之相关配置文件

    前面我们介绍了软件包管理.首先介绍了rpm包的相关命令,但是我们发现直接安装rpm包会被其依赖性折磨的不行,然后解决办法是yum在线管理,通过yum命令安装rpm包能自动帮助我们解决依赖性.最后又介绍 ...