文章概述

最近发现很久前一股脑地学习框架,发觉越发迷糊.知道了框架只是暂时的,重点是基础的技术.该文大篇幅回顾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. Python之scrapy实例1

    下文参考:http://www.jb51.net/article/57183.htm 个人也是稍加整理,修改其中的一些错误,这些错误与scrapy版本选择有关,个环境:Win7x64_SP1 + Py ...

  2. 59、jQuery初识

    jQuery是由原生js写的所以说所有jQuery制作出来的效果都可以使用js做出来,jQuery出现的目的是为了优化代码,提高码代码的效率它将很多功能封装. 一.jQuery的认识 1.何为jque ...

  3. Codeforces Round #439 (Div. 2) C DP(图论)

    C. The Intriguing Obsession time limit per test 1 second memory limit per test 256 megabytes input s ...

  4. web项目生成war包的问题

    今天面试一家公司,问我生成war包的命令是什么? 当时没明白,就说自己用的eclipse直接右键 export --->war 完了重启tomcat(第一种) 好久没用maven了.回来一查才明 ...

  5. 3分钟利用TurnipBit制作电子时钟

    转载请注明:@小五义 http://www.cnblogs.com/xiaowuyi 欢迎加入讨论群 64770604 TurnipBit(www.turnipbit.com)是一个面向青少年的开发板 ...

  6. MYSQL 子查询返回多列显示

    因工作需要,目前研究出一种mysql 技能,与大家分享一下. 需求:关联查询另一个大表数据的某些(一个以上)字段 方案:因关联查询的表数据太大.多表查询影响效率,单个子查询又有些多余.所以采用多列拼接 ...

  7. 主键乱序插入对Innodb性能的影响

    主键乱序插入对Innodb性能的影响 在平时的mysql文档学习中我们经常会看到这么一句话: MySQL tries to leave space so that future inserts do ...

  8. 在mac OS10.10下安装 cocoapods遇到的一些问题

    今天有个朋友问了我一个问题:为什么我安装cocoapods不成功,报 sh: line 1: 997 Abort trap: 6 /Applications/Xcode.app/Contents/De ...

  9. C++输入输出cin与cout

    输入对象 istream:cin(标准输入) 输出对象 ostream: cout(标准输出), cerr(标准错误),clog(输出程序运行时的一般性信息)

  10. linux学习(六)绝对路径、相对路径、cd、mkdir、rmdir、rm

    一.绝对路径 就是从根开始的,如:/root./usr/local. 二.相对路径 相对于当前路径的,比如我们在当前路径下建立了一个a.txt. [root@iZ25lzba47vZ ~]# pwd ...