美文网首页
Java入门系列12 -- Servlet

Java入门系列12 -- Servlet

作者: YanZi_33 | 来源:发表于2021-12-08 16:56 被阅读0次
    • Servlet是Server与Applet的缩写,意思是服务端小程序,使用Java语言编写的服务端程序,Servlet主要运行在服务端,并由服务器调用执行,是一种按照Servlet标准来开发的类,是SUN公司提供的一门用于开发动态Web资源的技术,言外之意就是要实现web开发,需要实现Servlet标准;
    • Servlet本质上也是Java类,但要遵循Servlet规范进行编写,没有main方法,它的创建,使用,销毁都由Servlet容器进行管理,言外之意就是写自己的类,不用写main方法,别人自动调用;
    • Servlet与HTTP协议是紧密联系的,其可以处理HTTP协议相关的所有内容,这也是Servlet广泛应用的原因;
    • 提供了Servlet功能的服务器,叫做Servlet容器,其常见的容器有很多,例如Tomcat,Jetty,WebLogic Server,WebSphere,JBoss等等;
    Servlet的实现
    • 第一步:创建Web项目,打开IDEA,File->New->Project,如下所示:
    Snip20211208_29.png
    • 选择Web Application(4.0),点击下一步;
    Snip20211208_30.png
    • 填写项目名称以及工作空间,点击Finish,进入工程主界面,如下所示:
    image.png
    • 第二步:实现Servlet规范,其目的是让自定义的类能够接受网络请求,接受到网络请求之后对请求进行分析,以及业务逻辑处理,具体步骤如下:

      • 创建一个Java类 命名为Servlet01,继承自HttpServlet
      • 重写service方法,进行业务逻辑处理;
      • 设置注解,即在完成业务代码编写之后,还需要向服务器说明,特定请求对应特定资源;
    • 开发servlet项目,在Servlet3.0中,可以使用@WebServlet注解将一个继承于javax.servlet.http.HttpServlet的类标注为可以处理用户请求的Servlet;

    • 案例代码如下:

    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * 实现Servlet
     * 1.创建普通Java类;
     * 2.实现Servlet规范,继承自HttpServlet;
     * 3.重写service方法,用来处理请求;
     * 4.设置注解;
     */
    @WebServlet("/ser01")
    public class Servlet01 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //这行代码要注释,否则浏览器报405错误
            //super.service(req, resp);
            System.out.println("Hello Servlet!!!");
            resp.getWriter().write("Hello Servlet!!!");
        }
    }
    
    image.png
    • 运行之后,会自动打开浏览器,在URL后面添加上面定义的注解ser01,浏览器中显示的内容,以及IDEA控制台也会有打印,如下所示:
    image.png
    • 上述的注解有多种不同的写法,以下写法都是等价的,如下:
    //@WebServlet("/ser01")
    //@WebServlet(name = "Servlet01",value = "ser01")
    //@WebServlet(name = "Servlet01",value = {"ser01","ser001"})
    @WebServlet(name = "Servlet01",urlPatterns = {"ser01","ser001"})
    
    Servlet--服务器的设置
    • 刚在浏览器中输入的URL为:http://localhost:8080/Servlet01_war_exploded/ser01,其中localhost:8080是本机地址与8080端口,Servlet01_war_exploded是项目的对外访问地址,ser01是注解,其中项目的对外访问地址是可以设置的,如下所示:
      Snip20211208_51.png
    Snip20211208_52.png
    • 再次运行项目,手动打开浏览器,然后输入http://localhost:8080/Servlet01/ser01即可;
    Servlet的工作流程
    • 当我们在浏览器中输入http://localhost:8080/Servlet01/ser01时,其本质是浏览器客户端向Tomcat服务器发送了一次Servlet请求,其工作原理如下:
      • 首先根据localhost定位到当前计算机;
      • 然后根据8080端口,定位到Tomcat服务器程序,因为在安装Tomcat时指定其默认端口为8080;
      • 接着根据Servlet01定位到指定项目;
      • 其次根据注解ser01,定位到该项目中的指定资源,然后执行如下:
      • 1)Web服务器(Tomcat)会先检查是否已经装载并创建了该Servlet的实例对象,如果是,则直接执行第4步,否则执行第2步;
      • 2)装载并创建了该Servlet的一个实例对象;
      • 3)调用Servlet实例对象的init()方法,进行初始化;
      • 4)创建一个用于封装HTTP请求消息的HTTPServletRequest对象和一个代表HTTP响应消息的HTTPServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去;
      • 5)WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法;
    Servlet的其他实现方式
    • 上面实现了一种创建Java类继承自HttpServlet的实现方式,还有其他的实现方式:
      • 创建Java类继承继承自GenericServlet
      • 创建Java类实现Servlet接口;
    • 案例代码如下:
    package com.sf.servlet;
    
    import javax.servlet.GenericServlet;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.annotation.WebServlet;
    import java.io.IOException;
    
    @WebServlet("/ser02")
    public class Servlet02 extends GenericServlet {
        @Override
        public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
            System.out.println("继承自GenericServlet的Servlet");
        }
    }
    
    package com.sf.servlet;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebServlet;
    import java.io.IOException;
    
    @WebServlet("/ser03")
    public class Servlet03 implements Servlet {
        @Override
        public void init(ServletConfig servletConfig) throws ServletException {
    
        }
    
        @Override
        public ServletConfig getServletConfig() {
            return null;
        }
    
        @Override
        public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
            System.out.println("实现Servlet接口的Servlet");
        }
    
        @Override
        public String getServletInfo() {
            return null;
        }
    
        @Override
        public void destroy() {
    
        }
    }
    
    Servlet的生命周期
    • 首先Servlet是没有main方法的,不能独立运行,它的运行完全由Servlet引擎来控制和调度,其生命周期如下所示:
    • 实例化和初始化:当前请求达到Web容器(Tomcat服务器)时,容器会查找该servlet实例对象是否存在,如果不存在,则会创建servlet实例对象并初始化,调用init()方法
    • 就绪/调用/服务阶段:容器调用servlet实例对象的service方法,处理请求的方法在整个生命周期中可以被多次调用;
    • 销毁:当容器关闭时(应用程序停止时),会将程序中的servlet实例对象进行销毁,调用destoty()方法
    • 案例代码:
    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @WebServlet("/ser05")
    public class Servlet05 extends HttpServlet {
    
        @Override
        public void destroy() {
            System.out.println("Servlet被销毁");
        }
    
        @Override
        public void init() throws ServletException {
            System.out.println("Servlet被初始化");
        }
    
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("Servlet正在处理请求");
        }
    }
    
    • 控制台调试结果如下:
    Snip20211209_32.png
    • 总结:Servlet的生命周期分为四步,Servlet类的加载->实例化->服务->销毁;
    • Tomcat与Servlet的工作时序图如下所示:
    Snip20211209_34.png
    • WebClient向Servlet容器(Tomcat)发出HTTP请求;
    • Servlet容器接收WebClient的请求;
    • Servlet容器创建一个HttpServletRequest对象,将WebClient请求信息封装到这个对象中;
    • Servlet容器创建一个HttpServletResponse对象,
    • Servlet容器调用HttpServlet对象的service方法,将Request与Response作为参数,传递给HttpServlet;
    • HttpServlet调用HttpServletRequest对象的有关方法,获取Http请求信息;
    • HttpServlet调用HttpServletResponse对象的有关方法,生成响应数据;
    • Servlet容器把HttpServlet的响应结果返回给WebClient;
    HttpServletRequest对象
    • HttpServletRequest对象:主要是用来接收客户端发送过来的请求信息,包括请求的参数,请求头等等,service()方法中形参接收的是HttpServletRequest接口的实例化对象,表示该对象主要应用在HTTP协议上,该对象是由Tomcat封装好,传递过来的;
    • 在HttpServletRequest接口中,定义的方法很多,但基本都是围绕接收客户端参数的,常用方法有如下:
      • getRequestURL:获取请求的完整URL,从http开始,到?结束;
      • getRequestURI:获取请求的部分路径,从项目站点名开始,到?结束;
      • getQueryString:获取请求的参数字符串;
      • getMethod:获取请求方式;
      • getProtocol:获取请求的协议版本;
      • getContextPath:获取项目的站点名(项目的对外访问路径);
      • getParameter:获取指定名称的参数;
      • getParameterValues:获取指定名称的参数的所有参数值;
    • 案例代码如下:
    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * 实现Servlet
     * 1.创建Java类
     * 2.实现Servlet规范,继承自HttpServlet
     * 3.重写service方法,用来处理请求
     * 4.设置注解,指定访问的路径
     */
    @WebServlet("/ser01")
    public class Servlet01 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("Hello Servlet!!!");
            resp.getWriter().write("Hello Servlet");
    
            System.out.println("请求的完整URL:" + req.getRequestURL().toString());
            System.out.println("请求的部分路径:" + req.getRequestURI());
            System.out.println("请求的参数字符串" + req.getQueryString());
            System.out.println("请求方式" + req.getMethod());
            System.out.println("请求的协议版本" + req.getProtocol());
            System.out.println("项目的站点名" + req.getContextPath());
      
            //获取指定名称的参数
            String name = req.getParameter("name");
            String password = req.getParameter("password");
            System.out.println("name:" + name + " password:" + password);
    
            //获取指定名称的参数的所有参数值
            String[] s = req.getParameterValues("s");
            if (s != null && s.length > 0) {
                for (String str : s) {
                    System.out.println("s:" + str);
                }
            }
        }
    }
    
    • 浏览器输入:http://localhost:8080/servlet01/ser01?name=admin&psd=asd123&s=sing&s=dance&s=speak,控制台打印结果如下:
    image.png
    请求乱码问题
    • Tomcat8.0及以上版本,GET请求在解析参数时不会出现乱码,但POST请求在解析参数时会出现乱码;

    • Tomcat8.0以下版本,GET请求在解析参数时会出现乱码,POST请求在解析参数时也会出现乱码;

    • Request请求属于接收客户端的参数,其有默认的语言编码为ISO-8859-1,此编码不支持中文,所以解析时肯定会出现乱码,解决方案有如下:

      • 在Request中设置编码方式,告诉服务器以指定的编码方式来解析数据,即req.setCharacterEncoding("UTF-8"),注意此设置只针对POST请求才有效;
      • 解决Tomcat8以下 get请求 参数解析乱码的方案:String sf_name = new String(req.getParameter("name").getBytes("ISO-8859-1"),"UTF-8")
    • 下面模拟一下POST请求,解析参数出现乱码的情况:创建JSP文件login.jsp,内容如下:

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>登录</title>
    </head>
    <body>
       <form method="post" action="ser01">
           姓名: <input type="text" name="name"> <br>
           密码: <input type="password" name="password"> <br>
           <button>登录</button>
       </form>
    </body>
    </html>
    
    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * 实现Servlet
     * 1.创建Java类
     * 2.实现Servlet规范,继承自HttpServlet
     * 3.重写service方法,用来处理请求
     * 4.设置注解,指定访问的路径
     */
    @WebServlet("/ser01")
    public class Servlet01 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("Hello Servlet!!!");
            //获取指定名称的参数
            String name = req.getParameter("name");
            String password = req.getParameter("password");
            System.out.println("name:" + name + " password:" + password);
        }
    }
    
    • 在浏览器中输入http://localhost:8080/servlet01/login.jsp,出现登录界面,账号密码均输入张三,然后IDEA控制台解析POST请求参数时出现乱码,如下所示:
    image.png
    • 现在将Servlet01中代码作如下修改:
    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @WebServlet("/ser01")
    public class Servlet01 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("Hello Servlet!!!");
            //request设置编码方式
            req.setCharacterEncoding("UTF-8");
            //获取指定名称的参数
            String name = req.getParameter("name");
            String password = req.getParameter("password");
            System.out.println("name:" + name + " password:" + password);
        }
    }
    
    • 然后再次在浏览器中输入,访问登录页,输入账号密码,Servlet容器在参数解析不会出现乱码了,如下所示:
    image.png
    • 解决Tomcat8以下 get请求 参数解析乱码(很少用了),代码如下:
    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @WebServlet("/ser01")
    public class Servlet01 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("Hello Servlet!!!");
            //request设置编码方式
            req.setCharacterEncoding("UTF-8");
            //获取指定名称的参数
            String name = req.getParameter("name");
            String password = req.getParameter("password");
            System.out.println("name:" + name + " password:" + password);
    
            //解决Tomcat8以下 get请求 参数解析乱码
            String sf_name = new String(req.getParameter("name").getBytes("ISO-8859-1"),"UTF-8");
            System.out.println("name:" + sf_name);
        }
    }
    
    请求转发
    • 请求转发:是一种服务器的行为,当客户端请求达到后,服务器进行转发,此时会将请求对象进行保存,地址栏中的URL地址不会改变,得到响应之后,服务器端再将响应发送给客户端,从始至终只有一个请求发出,实现了请求数据的共享,可实现页面的跳转
    • 案例代码:
    • Servlet04文件
    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @WebServlet("/ser04")
    public class Servlet04 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //接收参数
            String name = req.getParameter("name");
            System.out.println("Servlet04 name:" + name);
        }
    }
    
    • login.jsp文件
    <%--
      Created by IntelliJ IDEA.
      User: liyanyan33
      Date: 2021/12/8
      Time: 下午4:46
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$</title>
      </head>
      <body>
      $END$
      </body>
    </html>
    
    • login.html文件
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1> 登录 </h1>
    </body>
    </html>
    
    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @WebServlet("/ser03")
    public class Servlet03 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //接收参数
            String name = req.getParameter("name");
            System.out.println("Servlet03 name:" + name);
    
            //请求转发到Servlet04
            req.getRequestDispatcher("ser04").forward(req,resp);
    
            //请求转发给jsp文件
            req.getRequestDispatcher("login.jsp").forward(req,resp);
    
            //请求转发给html文件
            req.getRequestDispatcher("login.html").forward(req,resp);
        }
    }
    
    Request作用域
    • Request作用域可以看成是一个对象,通过该对象可以在一个请求中传递数据,作用范围是:在一次请求中有效,即服务器跳转有效;
    • 案例代码如下:
    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    
    @WebServlet("/ser05")
    public class Servlet05 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("Servlet05");
            //设置域对象的内容
            req.setAttribute("name","admin");
            req.setAttribute("age",20);
            List<String> list = new ArrayList<>();
            list.add("aaa");
            list.add("bbb");
            req.setAttribute("list",list);
    
            //设置请求转发Servlet06
            req.getRequestDispatcher("ser06").forward(req,resp);
    
            //设置请求转发jsp
        }
    }
    
    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.List;
    
    @WebServlet("/ser06")
    public class Servlet06 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("Servlet06");
            //获取域对象
            String name = (String) req.getAttribute("name");
            System.out.println("name: " + name);
    
            Integer age = (Integer) req.getAttribute("age");
            System.out.println("age: " + age);
    
            List<String> list = (List<String>) req.getAttribute("list");
            if (list != null && list.size() > 0) {
                System.out.println(list.get(0));
            }
        }
    }
    
    • 运行项目,在浏览器中输入http://localhost:8080/servlet01/ser05,结果如下:
    image.png
    HttpServletResponse对象
    • HttpServletResponse对象:是服务器端对客户端的请求进行响应,将Web服务器处理后的结果返回给客户端;
    • Servlet容器接收到客户端请求之后,可以通过HttpServletResponse对象直接进行响应,响应时需要获取输出流,输出流有两种形式:
      • 字符输出流:getWriter()
      • 字节输出流:getOutputStream()
    • 两种输出流不能同时使用,否则会报错;
    • 案例代码:
    package com.sf.servlet;
    
    import javax.servlet.Servlet;
    import javax.servlet.ServletException;
    import javax.servlet.ServletOutputStream;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    @WebServlet("/ser07")
    public class Servlet07 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //获取字符输出流
            PrintWriter writer = resp.getWriter();
            writer.write("Hello");
    
            //获取字节输出流
            ServletOutputStream outputStream = resp.getOutputStream();
            outputStream.write("World".getBytes());
            
            //不能同时使用 会报错
        }
    }
    
    响应乱码问题
    • 在响应中,若响应的内容中含有中文,有可能出现乱码问题,这时因为服务器响应的数据会经过网络传输,服务器端有一种编码方式,在客户端也有一种编码方式,当两端的编码方式不同时,就会出现乱码;
    • 针对字符输出流getWriter(),响应中文必定会出现乱码,因为服务端在进行编码时默认使用ISO-8859-1格式的编码,该编码只支持中文;
    • 解决方案:
      • 分别设置服务端响应与客户端响应的编码格式;
      • 同时设置服务端响应与客户端响应的编码格式;
    • 案例代码:
    package com.sf.servlet;
    
    import javax.servlet.Servlet;
    import javax.servlet.ServletException;
    import javax.servlet.ServletOutputStream;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    @WebServlet("/ser07")
    public class Servlet07 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //1.分别设置服务端与客户端的编码格式
            //设置服务端的编码格式
            resp.setCharacterEncoding("UTF-8");
            //设置客户端的编码格式
            resp.setHeader("content-type","text/html;charset=UTF-8");
    
            //2.同时设置服务端与客户端的编码格式
            resp.setContentType("text/html;charset=UTF-8");
    
            //获取字符输出流
            PrintWriter writer = resp.getWriter();
            writer.write("<h2>你好</h2>");
        }
    }
    
    • 针对字节输出流getOutputStream(),解决方案与上面的完全相同,代码如下:
    package com.sf.servlet;
    
    import javax.servlet.Servlet;
    import javax.servlet.ServletException;
    import javax.servlet.ServletOutputStream;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    @WebServlet("/ser07")
    public class Servlet07 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //1.分别设置服务端与客户端的编码格式
            //设置服务端的编码格式
            resp.setCharacterEncoding("UTF-8");
            //设置客户端的编码格式
            resp.setHeader("content-type","text/html;charset=UTF-8");
    
           //2.同时设置服务端与客户端的编码格式        
           resp.setContentType("text/html;charset=UTF-8");
    
            //获取字节输出流
            ServletOutputStream outputStream = resp.getOutputStream();
            outputStream.write("<h2>你好</h2>".getBytes());
        }
    }
    
    HttpServletResponse对象 -- 重定向
    • 重定向:是一种服务器指导,客户端的行为;
    • 当客户端发出第一个请求,被服务端接收处理之后,服务端会进行响应,在响应的同时,服务端会给客户端一个新的地址(下次请求的地址),当客户端接收到响应后,会立刻根据服务端给的新地址发起第二个请求,服务端接收请求并作出响应,重定向完成;
    • 在重定向中会存在两个请求,属于客户端行为;
    • 客户端浏览器的地址栏会发生改变;
    • Request对象不共享;
    • 案例代码如下:
    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.ServletOutputStream;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @WebServlet("/ser08")
    public class Servlet08 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("Servlet08");
            //获取请求参数
            String name = req.getParameter("name");
            System.out.println("name: " + name);
            //请求重定向到Servlet09
            resp.sendRedirect("ser09");
        }
    }
    
    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @WebServlet("/ser09")
    public class Servlet09 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("Servlet09");
            //获取请求参数
            String name = req.getParameter("name");
            System.out.println("name: " + name);
        }
    }
    
    • 在浏览器中输入http://localhost:8080/servlet01/ser08?name=yanzi,按下回车键,地址栏会变成http://localhost:8080/servlet01/ser09,且控制台打印如下:
    image.png
    请求转发与重定向的区别
    • 不同点:
      • 请求转发是一次请求,数据在Request域中共享,重定向是两次请求,数据在Request域中不共享;
      • 请求转发是服务端行为,重定向是客户端行为;
      • 请求转发时地址栏不发生变化,重定向时地址栏会发生变化;
      • 请求转发时的地址只能是当前站点(当前项目)下的资源,重定向时的地址可以是任意的地址,能实现跨域;
    • 相同点:
      • 两者都能实现跳转;
    Cookie对象
    • Cookie是浏览器提供的一种技术,通过服务器的程序能将一些只须保存在客户端,或者在客户端进行处理的数据,放在本地计算机上,不需要通过网络传输,因而提高网页处理的效率,并且能够减少服务器的负载,但是由于Cookie是服务端保存在客户端的信息,所以其安全性也是很差的,例如常见的记住密码则可以通过Cookie来实现;

    • 有一个专门操作Cookie的类javax.servlet.http.Cookie,跟随服务端的响应发送给客户端,保存在浏览器,当下次再访问服务器是把Cookie再带回服务器;

    • Cookie的格式:键值对,多个键值对用分号隔开;

    • Cookie的创建与发送:通过new Coookie()方法来创建Cookie对象,然后添加到response对象中,接着随着响应发送给客户端;

    • 案例代码如下:

    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @WebServlet("/ser02")
    public class Servlet02 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("Hello Servlet!!!");
            //创建Cookie
            Cookie cookie = new Cookie("name","admin");
            //添加到response 发送给客户端
            resp.addCookie(cookie);
        }
    }
    
    • 运行项目,在Google浏览器中输入http://localhost:8080/Servlet01/ser02,然后浏览器中,右键->检查,如下所示:
    image.png
    • Cookie的获取:在服务端只提供了一个getCookies()的方法来获取客户端回传的所有cookie组成的一个数组,如果需要获取单个cookie则需要通过遍历,getName()获取Cookie的名称,getValue()获取Cookie的值;
    • 案例代码如下:
    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @WebServlet("/ser02")
    public class Servlet02 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("Hello Servlet!!!");
            //获取cookie数组
            Cookie[] cookies = req.getCookies();
            if (cookies != null && cookies.length > 0) {
                for (Cookie cookie: cookies) {
                    String name = cookie.getName();
                    String value = cookie.getValue();
                    System.out.printf("cookie名称:" + name + ",值:" + value);
                }
            }
        }
    }
    
    • Cookie设置到期时间:Cookie存在有效时间的,默认为当前浏览器关闭就会失效,我们可以手动设置cookie的有效时间,通过setMaxAge(int time)方法来设置cookie的最大有效时间,以秒为单位;
    • 到期时间的取值情况如下 :
      • 负整数:表示不存储该cookie,其maxAge属性的默认值为就是-1,表示只在浏览器内存中存活,一旦关闭浏览器窗口,那么cookie就会消失;
      • 正整数:表示存储的秒数,浏览器会把cookie保存到硬盘上,就算关闭浏览器,就算重启客户端电脑,cookie也会存活相应的时间;
      • 等于0,表示删除该cookie,浏览器内存中和客户端硬盘上都会删除该cookie对象;
    • 案例代码如下:
    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @WebServlet("/cookie03")
    public class Cookie03 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            Cookie cookie1 = new Cookie("name1","zhangsan");
            cookie1.setMaxAge(-1);
            resp.addCookie(cookie1);
    
            Cookie cookie2 = new Cookie("name2","lisi");
            cookie2.setMaxAge(60);
            resp.addCookie(cookie2);
    
            Cookie cookie3 = new Cookie("name3","wangwu");
            cookie3.setMaxAge(0);
            resp.addCookie(cookie3);
        }
    }
    
    • Cookie的注意点
      • cookie保存在当前浏览器中,不能跨浏览器保存;
      • cookie存中文有问题,正常情况下cookie是不能出现中文的,如果有中文则通过URLEncoder.encode()来进行编码,获取时通过URLDecoder.decode()来进行解码;
      • 同名cookie问题,如果服务端发送重复的cookie那么会覆盖原来的cookie;
      • 浏览器存放cookie的数量是有上限的,cookie时存储在客户端的,一般由服务端创建和设定,后期结合Session来实现回话跟踪;
      • cookie存储的数据是有大小限制的,一般上限为4KB左右;
    • 案例代码:
    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.net.URLDecoder;
    import java.net.URLEncoder;
    
    @WebServlet("/cookie04")
    public class Cookie04 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String name = "姓名";
            String vaule = "李四";
    
            //编码
            name = URLEncoder.encode(name);
            vaule = URLEncoder.encode(vaule);
    
            Cookie cookie = new Cookie(name,vaule);
            resp.addCookie(cookie);
    
            Cookie[] cookies = req.getCookies();
            if (cookies != null && cookies.length > 0) {
                for (Cookie cook: cookies) {
                    //解码
                    System.out.println(URLDecoder.decode(cook.getName()));
                    System.out.println(URLDecoder.decode(cook.getValue()));
                }
            }
        }
    }
    
    • Cookie的路径:通过setPath()方法设置cookie的路径,这个路径直接决定了服务器的请求是否会从浏览器中加载某些cookie
    • 情景一:当前服务器下任何项目的任意资源都可以获取cookie对象;
    • 情景二:当前项目下的资源可获取cookie对象,默认不设置cookie的路径;
    • 情景三:指定项目下的资源可获取cookie对象;
    • 情景四:指定目录下的资源可获取cookie对象;
    • 总结:只有访问的路径中包含Cookie对象的Path,才可以获取到Cookie对象;
    • 案例代码:
    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.net.URLDecoder;
    import java.net.URLEncoder;
    
    @WebServlet("/cookie05")
    public class Cookie05 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
            //当前服务器下任何项目的任意资源都可以获取cookie对象
            Cookie cookie1 = new Cookie("cookie01","cooki01");
            cookie1.setPath("/");
            resp.addCookie(cookie1);
    
            //当前项目下的资源可获取cookie对象,默认不设置cookie的路径
            //http://localhost:8080/servlet01/
            Cookie cookie2 = new Cookie("cookie02","cooki02");
            cookie2.setPath("/servlet01");
            resp.addCookie(cookie2);
    
            //指定项目下的资源可获取cookie对象
            Cookie cookie3 = new Cookie("cookie03","cooki03");
            cookie3.setPath("/servlet02");
            resp.addCookie(cookie3);
    
            //当前项目 指定目录下的资源可获取cookie对象
            Cookie cookie4 = new Cookie("cookie04","cooki04");
            cookie4.setPath("/servlet01/ser01");
            resp.addCookie(cookie4);
        }
    }
    
    HttpSession对象
    • HttpSession对象:是javax.servlet.http.HttpSession的实例,属于HTTP协议的范畴;
    • 对于服务器而言,每一个脸接到它的客户端都是一个session,servlet容器使用此接口创建HTTP客户端和HTTP服务器之间的会话,会话将保留指定的时间段,跨多个连接或来自用户的页面请求,一个会话通常对应一个用户,该用户可能多次访问一个站点,可以通过此接口查看和操作某个会话的信息,比如会话标识符,创建时间和最后一次访问时间,在整个Session中,最重要的就是属性的操作;
    • session无论客户端还是服务端都可以感知到,若重新打开一个新的浏览器,则无法获取之前的session,因为每一个session只保存在当前浏览器中,并在相关的页面取得;
    • session的作用是为了标识一次会话,或者说是确认一个用户,在一次会话(一个用户的多次请求)期间共享数据,我们可通过request.getSession()方法,来获取当前会话的session对象;
    • 案例代码如下:
    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    
    @WebServlet("/session01")
    public class Session01 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //获取会话对象
            HttpSession session = req.getSession();
            //获取会话id
            System.out.println(session.getId());
            //获取会话创建的时间
            System.out.println(session.getCreationTime());
            //获取会话最后一次访问时间
            System.out.println(session.getLastAccessedTime());
            //判断是否是新的会话
            System.out.println(session.isNew());
        }
    }
    
    • 获取session对象时,若session对象存在时,则直接获取,若session对象不存在时,则创建session对象;
    标识符JSEESIONID
    • session是为了标识一次会话,其标识就是会话id即sessionId;
    • 每当一次请求到达服务器时:
    • 如果开启了会话,服务器第一步会查看是否从客户端回传一个名为JSEESIONID的cookie,
      • 如果没有则认为这是一次新的会话,会创建一个新的session对象,并用唯一的sessionid为此会话作一个标志;
      • 如果有JSEESIONID这个cookie回传,服务器则会根据JSEESIONID这个值去查看是否含有id为JSEESIONID值的session对象,如果没有则认为是一个新的会话,则重新创建一个新的session对象,并用唯一的sessionid为此会话作一个标志,如果找到了对应的session对象,则认为是之前标志过的一次会话,返回该session对象,数据实现共享;
    • JSEESIONID是一个cookie,这是一个比较特殊的cookie,当请求到达服务器时,如果访问了session,则服务器会创建一个名为JSEESIONID,值为获取到的session(无论是获取到的还是新创建的)的sessionid的cookie对象,并添加到response对象中,响应给客户端,有效时间为关闭浏览器;
    • Session的底层是依赖Cookie来实现的;
    image.png
    session域对象
    • Session表示一次会话,在一次会话过程中数据是可以共享的,这时session作为域对象存在,可以通过setAttribute(name,value)方法往域对象中添加数据,通过getAttribute(name)从域对象中获取数据,通过removeAttribute(name)从域对象中移除数据;
    • 数据存储在session域对象中,当session对象不存在了,或者两个不同的session,数据也就不能共享了;
    • 请求转发时的案例如下:
    <%--
      Created by IntelliJ IDEA.
      User: liyanyan33
      Date: 2021/12/8
      Time: 下午4:46
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$</title>
      </head>
      <body>
       <%
         //获取session域对象
         String s_name = (String) request.getSession().getAttribute("s_name");
         String s_password = (String) request.getSession().getAttribute("s_password");
         out.print("s_name: " + s_name + ",s_password: " + s_password);
    
         //获取request域对象
         String r_name = (String) request.getAttribute("r_name");
         out.print(",r_name: " + r_name);
       %>
      </body>
    </html>
    
    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    
    @WebServlet("/session02")
    public class Session02 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //获取会话对象
            HttpSession session = req.getSession();
            //设置session域对象
            session.setAttribute("s_name","lisi");
            session.setAttribute("s_password","asd123");
            //移除session域对象
            session.removeAttribute("s_password");
    
            //设置request域对象
            req.setAttribute("r_name","zhangsan");
    
            //请求转发
            req.getRequestDispatcher("index.jsp").forward(req,resp);
        }
    }
    
    • 浏览器输入http://localhost:8080/servlet01/session02,调试结果如下:
      image.png
    • 请求重定向时,代码修改如下:
    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    
    @WebServlet("/session02")
    public class Session02 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //获取会话对象
            HttpSession session = req.getSession();
            //设置session域对象
            session.setAttribute("s_name","lisi");
            session.setAttribute("s_password","asd123");
            //移除session域对象
            session.removeAttribute("s_password");
    
            //设置request域对象
            req.setAttribute("r_name","zhangsan");
    
            //请求重定向
            resp.sendRedirect("index.jsp");
        }
    }
    
    • 浏览器输入http://localhost:8080/servlet01/session02,调试结果如下:
    image.png
    • 总结:
    • 请求转发时,request作用域有效,session作用域有效;
    • 请求重定向时,request作用域失效,session作用域有效;
    Session对象的销毁
    • 默认到期时间:当客户端第一次请求servlet且操作session时,session对象生成,Tomcat中session默认存活时间为30分钟,一旦操作,session就会重新计时;
    • 默认到期时间可以在Tomcat/conf/web.xml文件中进行修改的,如下所示:
    <session-config>
       <session-timeout>30</session-timeout>
    </session-config>
    
    • 手动设置到期时间:通过设置session的最大不活动时间,调用setMaxInactiveInterval()方法,单位为秒;
    • 立即失效:调用invalidate()方法,让session立即失效;
    • 关闭浏览器就会失效:session底层依赖cookie,cookie在浏览器关闭时就会失效;
    • 关闭服务器就会失效
    ServletContext对象
    • 每一个Web应用都有且仅有一个ServletContext对象,又称为Application对象,其与应用程序相关,在Web容器启动时,会为每一个Web应用程序创建一个对应的ServletContext对象;
    • ServletContext对象有两大作用:
      • 作用域对象实现共享数据,此数据在整个应用程序中实现共享;
      • 该对象中保存了当前应用程序的相关信息,可通过相关Api获取;
    • 案例代码如下:
    package com.sf.servlet;
    
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @WebServlet("/scontext01")
    public class ServletContext01 extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //获取ServletContext对象
            //1.通过request对象获取
            ServletContext context1 = req.getServletContext();
    
            //2.通过session对象获取
            ServletContext context2 = req.getSession().getServletContext();
    
            //3.通过ServletConfig获取
            ServletContext context3 = getServletConfig().getServletContext();
    
            //4.直接获取
            ServletContext context4 = getServletContext();
    
            //常用方法
            //获取当前服务器的版本信息
            String serverInfo = context1.getServerInfo();
            System.out.println("当前服务器的版本信息: " + serverInfo);
            //获取项目的真实路径
            String realPath = context1.getRealPath("/");
            System.out.println("项目的真实路径:" + realPath);
        }
    }
    
    • 控制台打印结果如下:
    image.png
    ServletContext域对象
    • ServletContext可以作为域对象,能存数据,使得整个应用程序共享数据,但不建议存放过多的数据,因为ServletContext中的数据一旦存储进去没有手动移除将会一直存在;
    • Servlet中有三大域对象:
      • request域对象,请求转发时有效,重定向时失效;
      • session域对象,请求转发和重定向时均有效,session销毁时失效;
      • ServletContext域对象,在整个应用程序中有效,服务器关闭时失效;
    //1.通过request对象获取
    ServletContext context1 = req.getServletContext();
    context1.setAttribute("name","yanzi");
    context1.setAttribute("password","asd123");
    context1.removeAttribute("password");
    String name = (String) context1.getAttribute("name");
    
    文件的上传
    • 文件上传涉及到前台页面的编写和后台服务端代码的编写,前台发送文件,后台接收并保存文件,实现文件的完整上传;
    • 首先文件上传的前台页面编写如下:
    • 创建一个upload.html文件,代码如下:
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>文件上传</title>
    </head>
    <body>
       <form method="post" enctype="multipart/form-data" action="uploadServlet">
           姓名: <input type="text" name="name"> <br>
           文件: <input type="file" name="myfile"> <br>
           <button>提交</button>
       </form>
    </body>
    </html>
    
    • 首先是定义表单;

    • 设置表单的提交类型为post;

    • 设置表单类型为文件上传表单 enctype="multipart/form-data";

    • 设置文件提交的地址;

    • 准备表单元素:

      • 普通表单项 type="text";
      • 文件表单项 type="file"
    • 设置表单项的name属性值,否则后台无法接收数据;

    • 然后文件上传的后台代码编写:

    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.MultipartConfig;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.Part;
    import java.io.IOException;
    
    @WebServlet("/uploadServlet")
    @MultipartConfig
    public class UploadServlet extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("文件上传");
            //设置请求的编码
            req.setCharacterEncoding("UTF-8");
            //获取普通表单项
            String name = req.getParameter("name");
            System.out.println("name: " + name);
            //获取上传文件
            Part part = req.getPart("myfile");
            //通过part对象 获取上传的文件名
            String fileName = part.getSubmittedFileName();
            System.out.println("上传文件名: " + fileName);
            //文件存放路径
            String filePath = req.getServletContext().getRealPath("/");
            System.out.println("文件存放路径: " + filePath);
            //上传文件到执行目录
            part.write(filePath + "/" + fileName);
        }
    }
    
    • 运行项目,在浏览器中输入http://localhost:8080/Servlet07/upload.html,然后选择文件text.txt,点击提交,进入后台,IDEA控制台打印如下:
      image.png
    • 查看文件存放路径,确实上传成功;
    文件下载
    • 将服务器上的资源文件下载到本地,可通过超链接进行下载,或者通过后台代码下载
    • 超链接下载:使用a标签;
      • 当超链接遇到浏览器能识别的资源时(例如txt,png,jpg文件能识别),默认不会下载,可通过download属性进行下载;
      • 当超链接遇到浏览器不能识别的资源时,会自动下载;
    • 首先准备资源文件并设置路径,如下所示:
    image.png
    • 然后创建download.html文件,内容如下:
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>文件下载</title>
    </head>
    <body>
       <a href="download/test.txt">文本文件</a>
       <a href="download/tk.png">图片文件</a>
       <a href="download/xxx.zip">压缩文件</a>
       <hr>
       <a href="download/test.txt" download=>文本文件</a>
       <a href="download/tk.png" download="推客.png">图片文件</a>
    </body>
    </html>
    
    • 浏览器输入http://localhost:8080/Servlet07/download.html,看到如下:
    image.png
    • 后台代码下载的实现,创建DownloadServlet文件;
      • 需要设置响应类型,浏览器无法使用某种方式或激活某个程序来处理的MIME类型,即resp.setContentType("application/x-msdownload")
      • 需要设置响应头,即resp.setHeader("Content-Disposition","attachment;filename=" + fileName)
      • 读取下载的文件,调用resp.getOutputStream()向客户端写入内容;
    • 案例代码如下:
    package com.sf.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.ServletOutputStream;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    @WebServlet("/downloadServlet")
    public class DownloadServlet extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("文件下载");
            //设置请求编码格式
            req.setCharacterEncoding("UTF-8");
            resp.setContentType("text/html;charset=UTF-8");
            //获取文件名
            String fileName = req.getParameter("fileName");
            if (fileName == null || "".equals(fileName.trim())) {
                resp.getWriter().write("请输入要下载的文件名");
                resp.getWriter().close();
                return;
            }
            System.out.println("文件名: " + fileName);
            //获取图片的存放路径
            String path = req.getServletContext().getRealPath("/download/");
            System.out.println("目标文件路径: " + path);
            //通过路径获取file对象
            File file = new File(path + fileName);
            if (file.exists() && file.isFile()) {
                resp.setContentType("application/x-msdownload");
                resp.setHeader("Content-Disposition","attachment;filename=" + fileName);
                //输入流
                InputStream in = new FileInputStream(file);
                //字节输出流
                ServletOutputStream out = resp.getOutputStream();
                //byte数组
                byte[] bytes = new byte[1024];
                int length = 0;
                while ((length = in.read(bytes)) != -1) {
                    //输出
                    out.write(bytes,0,length);
                }
                //关闭资源
                out.close();
                in.close();
            } else {
                resp.getWriter().write("文件不存在,请重试");
                resp.getWriter().close();
            }
        }
    }
    
    • download.html文件代码如下:
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>文件下载</title>
    </head>
    <body>
       <a href="download/test.txt">文本文件</a>
       <a href="download/tk.png">图片文件</a>
       <a href="download/xxx.zip">压缩文件</a>
       <hr>
       <a href="download/test.txt" download=>文本文件</a>
       <a href="download/tk.png" download="推客.png">图片文件</a>
    
       <form action="downloadServlet">
           文件名: <input type="text" name="fileName" placeholder="请输入要下载的文件名">
           <button>下载</button>
       </form>
    
    </body>
    </html>
    
    • 运行项目,浏览器中输入http://localhost:8080/Servlet07/download.html,然后输入文件名test.txt,进行文件下载;
    image.png

    相关文章

      网友评论

          本文标题:Java入门系列12 -- Servlet

          本文链接:https://www.haomeiwen.com/subject/lautfrtx.html