• 编程语言 : 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("
    ");  
          out.write("<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    ");  
          out.write("<html>
    ");  
          out.write("<head>
    ");  
          out.write("<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    ");  
          out.write("<title>Insert title here</title>
    ");  
          out.write("</head>
    ");  
          out.write("<body>
    ");  
          out.write("<h1>JSP脚本元素</h1>
    ");  
          out.write("
    ");  
          out.write("
    ");  
          out.write("<!-- 表达式 等价于 会被翻译为 out.print -->
    ");  
          out.print("abcd" );  
          out.write("
    ");  
          out.write("
    ");  
      
        // JSP 脚本代码块,嵌入任何java代码  
        String s = "abcdefg";  
        s = s.toUpperCase();  
        out.print(s);  
      
          out.write("
    ");  
          out.write("</body>
    ");  
          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:a1.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:a1.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;  //upload23  upload35
            //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;  //upload23  upload35
            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>
    Rudolph Browne, an idealist and optimist.
  • 相关阅读:
    白书数据结构基础总结
    UVA 10557 XYZZY 结题报告
    UVA 10047 The Monocycle 解题报告
    二叉查找树及其C语言实现
    堆排序及其c语言实现
    约瑟夫环问题小结
    KMP算法总结
    UVA 10129 Play on Words 解题报告
    深入浅出Node.js (8)
    洛谷 P1757 通天之分组背包
  • 原文地址:https://www.cnblogs.com/CARPE-DIEM-wu/p/7704488.html
Copyright © 2020-2023  润新知