Servlet基础

news2024/11/13 18:11:57

Servlet

  • 1. Servlet概述
  • 2. 快速入门
  • 3. 执行原理
  • 4. 生命周期方法
  • 5. Servlet3.0
  • 6. 体系结构
  • 7. 相关配置
  • 8. HTTP
    • 8.1 概念
    • 8.2 Request
    • 8.3 Response
    • 8.4 ServletContext
    • 综合案例:文件下载:

1. Servlet概述

  1. Servlet是JavaEE规范(接口)之一
  2. Servlet是JavaWeb三大组件之一,三大组件分别是Servlet程序、Filter过滤器、Listener监听器
  3. Servlet是运行在服务器上的一个Java程序,可以接收客户端发来的请求,并响应数据给客户端

2. 快速入门

步骤:

  1. 定义一个类,实现Servlet接口,并实现接口中的抽象方法
public class MyServletDemo1 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("你好,MyServletDemo1.....");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}
  1. 在WEB-INF文件夹中的web.xml文件中配置Servlet程序的访问地址
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--配置Servlet-->
    <!--servlet标签给Tomcat配置Servlet程序-->
    <servlet>
        <!--servlet-name标签给Servlet程序起一个别名(一般是类名)-->
        <servlet-name>myServlet</servlet-name>
        <!--servlet-class标签是Servlet程序的全类名-->
        <servlet-class>com.ming.web.servlet.MyServletDemo1</servlet-class>
    </servlet>
    <!--servlet-mapping标签是servlet的映射,给Servlet程序配置访问地址-->
    <servlet-mapping>
        <!--此时servlet-name标签要与上一个servlet-name标签中相同-->
        <servlet-name>myServlet</servlet-name>
        <!--url-pattern标签配置访问地址:/myServlet,
            也就是说在浏览器的地址栏中输入'http://localhost:8080/servlet_demo_1/myServlet'访问到的是MyServletDemo1类-->
        <url-pattern>/myServlet</url-pattern>
    </servlet-mapping>
</web-app>
  1. 访问路径,输出结果。你好,MyServletDemo1.....

3. 执行原理

简图:
原理
文字说明:

  1. 当服务器接收到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径
  2. 查找web.xml文件,是否有对应的 标签体内容
  3. 如果有,则在找到对应的全类名
  4. tomat会将字节码文件加载进内存,并且创建其对象
  5. 调用其方法

4. 生命周期方法

  1. 被创建:执行init方法,只执行一次
    • Servlet什么时候被创建?
      • 默认情况下,第一次被访问时,Servlet被创建
      • 可以配置执行Servlet的创建时机。
        <servlet>
            <servlet-name>myServlet</servlet-name>
            <servlet-class>com.ming.web.servlet.MyServletDemo1</servlet-class>
            <!--指定Servlet的创建时机
                1. 第一次被访问时,创建 :设置成负数
                2. 在服务器启动时,创建 :设置成正数或0
             -->
            <load-on-startup>1</load-on-startup>
        </servlet>
        
    • Servlet的init方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的。(多个用户同时访问时,可能存在线程安全问题)
  2. 提供服务:执行service方法,执行多次
    • 每次访问Servlet时,service方法都会被调用一次。
  3. 被销毁:执行destroy方法,只执行一次
    • Servlet被销毁时执行。服务器关闭时,Servlet被销毁
    • 只有服务器正常关闭时,才会执行destroy方法
    • destroy方法在Servlet被销毁之前执行,一般用于释放资源

5. Servlet3.0

  • 好处:
    • 支持注解配置。可以不需要web.xml.
  • 步骤:
    1. 创建JavaEE项目,选择Servlet版本3.0以上,可以不创建web.xml
    2. 定义一个类,实现Servlet接口,实现抽象方法
    3. 在类上加上注解 @WebServlet("/资源路径")
      代码展示:
@WebServlet("/myServletDemo3")
public class MyServletDemo3 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("注解配置.........");
    }
    @Override
    public String getServletInfo() {
        return null;
    }
    @Override
    public void destroy() {
    }
}

6. 体系结构

简图

7. 相关配置

  1. urlpartten:Servlet访问路径
    • 一个Servlet可以定义多个访问路径:@WebServlet({"/d1","/dd1","/ddd1"})
    • 路径定义规则:(/XXX,/XXX/XXX,*.do)

8. HTTP

8.1 概念

Hyper Transfer Protocol 超文本传输协议

  • 传输协议:定义了,客户端和服务器端通信时,发送数据的格式。HTTP协议中的数据又称为报文
  • 特点:
    • 基于TCP/IP的高级协议
    • 默认端口号:80
    • 基于请求/响应模型的:一次请求对应一个响应
    • 无状态的,每次请求之间相互独立,不能交互数据
  • 历史版本:
    • 1.0 : 每一次请求响应都会建立新的连接
    • 1.1 : 复用连接(长连接)

请求消息数据格式

字符串格式:
POST /myServletDemo2 HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 12
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://localhost:8080
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:8080/login.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9

说明:

  1. 请求行
    请求方式 请求url 请求协议/版本
    GET /myServletDemo1 HTTP/1.1

    • 请求方式:
      • HTTP协议有7种请求方式,常用的有2种
        • GET
          • 请求参数在请求行中,在url后。
          • 请求的url长度有限制的
          • 不太安全
        • POST
          • 请求参数在请求体中。
          • 请求的url长度没有限制
          • 相对安全
  2. 请求头:客户端浏览器告诉服务器一些信息
    请求头名称:请求头值
    Host: localhost:8080

    • 常见的请求头:
      • User-Agent:浏览器告诉服务器,我访问你使用的浏览器版本信息
        • 作用:可以在服务器端获取该头的信息,解决浏览器的兼容性问题
      • Referer: http://localhost:8080/login.html : 告诉服务器,我(当前请求)从哪里来?
        • 作用:防盗链,统计工作
  3. 请求空行
    空行,就是用于分割POST请求的请求头和请求体的。

  4. 请求体(正文)
    GET :没有请求体
    POST :请求体是请求参数

8.2 Request

(1) request对象和response对象的原理

  • request和response对象是由服务器创建的。我们来使用它们
  • request对象是来获取请求消息,response对象是来设置响应消息

(2) request的继承体系结构

	public interface ServletRequest{}
	public interface HttpServletRequest extends ServletRequest {}
	// 具体实现由tomcat来完成
	// org.apache.catalina.connector.RequestFacade
	public class RequestFacade implements HttpServletRequest {}

(3) request 功能:

  • 获取请求行数据(GET /demo/myServletDemo1?name=zhansan HTTP/1.1)

    • String getMethod() - 获取请求方式:GET
    • String getContextPath() - 获取虚拟目录:/demo
    • String getServletPath() - 获取Servlet路径:/myServletDemo1
    • String getQueryString() - 获取get方式请求参数:name=zhansan
    • String getRequestURI() - 获取请求URI:/demo/myServletDemo1
    • StringBuffer getRequestURL() - 获取请求URL: http://localhost:8080/demo/myServletDemo1
    • String getProtocol() - 获取协议及版本:HTTP/1.1
    • String getRemoteAddr() - 获取客户机的IP地址

    最后两个是ServletRequest里的方法,其余的是HttpServletRequest里的方法
    URL: 统一资源定位符:http://localhost:8080/demo/myServletDemo1 中华人民共和国
    URI: 统一资源标识符:/demo/myServletDemo1 共和国

    代码展示:

    // 获取请求行数据方法演示
    @WebServlet("/requestServletDemo1")
    public class RequestServletDemo1 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 1. 获取请求方式:GET
            String method = request.getMethod();
            System.out.println(method);
            // 2. 获取虚拟目录:/demo
            String contextPath = request.getContextPath();
            System.out.println(contextPath);
            // 3. 获取Servlet路径:/requestServletDemo1
            String servletPath = request.getServletPath();
            System.out.println(servletPath);
            // 4. 获取get方式请求参数:name=zhansan
            String queryString = request.getQueryString();
            System.out.println(queryString);
            // 5. 获取请求URI:/demo/requestServletDemo1
            String requestURI = request.getRequestURI();
            System.out.println(requestURI);
            // 6. 获取请求URL: http://localhost:8080/demo/requestServletDemo1
            StringBuffer requestURL = request.getRequestURL();
            System.out.println(requestURL);
            // 7. 获取协议及版本:HTTP/1.1
            String protocol = request.getProtocol();
            System.out.println(protocol);
            // 8. 获取客户机的IP地址:0:0:0:0:0:0:0:1
            String remoteAddr = request.getRemoteAddr();
            System.out.println(remoteAddr);
        }
    }
    
  • 获取请求头数据

    • String getHeader(String name) : 通过请求头的名称获取请求头的值
    • Enumeration<String> getHeaderNames() : 获取所有的请求头名称

    代码展示:

    // 获取请求头数据 方法演示
    @WebServlet("/requestServletDemo2")
    public class RequestServletDemo2 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 演示获取请求头数据
            // 1. 获取所有的请求头名称
            Enumeration<String> headerNames = request.getHeaderNames();
            // 2. 遍历
            while(headerNames.hasMoreElements()){
                String name = headerNames.nextElement();
                // 根据名称获取请求头的值
                String value = request.getHeader(name);
                System.out.println(name+":"+value);
            }
        }
    }
    
    // 利用user-agent解决版本兼容问题 
    @WebServlet("/requestServletDemo3")
    public class RequestServletDemo3 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 演示获取请求头数据
            String agent = request.getHeader("user-agent");
            // 判断agent 的浏览器版本
            if (agent.contains("Chrome")){
                // 谷歌
                System.out.println("谷歌来了....");
            }else if(agent.contains("Firefox")){
                // 火狐
                System.out.println("火狐来了....");
            }
        }
    }
    
    // 利用请求头的referer,解决防盗链问题
    @WebServlet("/requestServletDemo4")
    public class RequestServletDemo4 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 演示获取请求头数据 referer
            String referer = request.getHeader("referer");
            System.out.println(referer);
            // 防盗链
            if (referer!=null) {
                if (referer.contains("/demo")) {
                    // 正常访问
                    System.out.println("播放电影....");
                } else {
                    // 盗链
                    System.out.println("无法观看....");
                }
            }
        }
    }
    
  • 获取请求体数据

    • 只有POST请求方式,才有请求体
    • 步骤
      • 获取流对象
        • BufferedReader getReader() : 获取字符输入流,只能操作字符数据
        • ServletInputStream getInputStream() : 获取字节输入流,可以操作所有类型数据(文件上传)
      • 再从流对象中拿数据

    代码展示:

    // 获取请求体数据
    @WebServlet("/requestServletDemo5")
    public class RequestServletDemo5 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 获取请求消息体 -- 请求参数
            // 1. 获取字符流
            BufferedReader reader = request.getReader();
            // 2. 读取数据
            String line = null;
            while((line=reader.readLine())!=null){
                System.out.println(line);
            }
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
        }
    }
    
    <!-- html页面 -->
    <form action="/demo/requestServletDemo5" method="post">
        名字:<input type="text" name="username"><br>
        密码:<input type="text" name="password"><br>
        <input type="submit" value="注册">
    </form>
    
  • 获取请求参数通用方式

    • String getParameter(String name):根据参数名称获取参数值,username=zs&password=123
    • String[] getParameterValues(String name):根据参数名称获取参数值的数值,hobby=XX&hobby=YY
    • Enumeration<String> getParameterNames():获取所有请求的参数名称
    • Map<String, String[]> getParameterMap():获取所有参数的map集合

    中文乱码问题
    get方式:tomcat 8 已经将get方式乱码问题解决了
    post方式:会乱码,解决:在获取参数前,设置request的编码 request.setCharacterEncoding("utf-8");

    代码展示:

    @WebServlet("/requestServletDemo6")
    public class RequestServletDemo6 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 解决中文乱码问题
            // get方式:tomcat 8 已经将get方式乱码问题解决了
            // post方式:会乱码,
            //      解决:在获取参数前,设置request的编码
            request.setCharacterEncoding("utf-8");
    
            // 根据参数名称获取参数值
            String username = request.getParameter("username");
            System.out.println("post方式获取username:"+username);
            System.out.println("------------------------");
            // 根据参数名称获取参数值的数值
            String[] hobbies = request.getParameterValues("hobby");
            System.out.println("hobby:");
            for (String hobby : hobbies) {
                System.out.println(hobby);
            }
            System.out.println("------------------------");
            // 获取所有请求的参数名称
            Enumeration<String> parameterNames = request.getParameterNames();
            while(parameterNames.hasMoreElements()){
                String name = parameterNames.nextElement();
                String[] values = request.getParameterValues(name);
                System.out.println(name);
                for (String value : values) {
                    System.out.println(value);
                }
                System.out.println("*********");
            }
            System.out.println("------------------------");
            // 获取所有参数的map集合
            Map<String, String[]> parameterMap = request.getParameterMap();
            Set<String> keySet = parameterMap.keySet();
            for (String name : keySet) {
                // 获取键,获取值
                String[] values = parameterMap.get(name);
                System.out.println(name);
                for (String value : values) {
                    System.out.println(value);
                }
                System.out.println("+++++++++");
            }
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 根据参数名称获取参数值
    //        String username = request.getParameter("username");
    //        System.out.println("get方式获取username:"+username);
            this.doPost(request,response);
        }
    }
    
    
    <form action="/demo/requestServletDemo6" method="post">
        名字:<input type="text" name="username"><br>
        密码:<input type="text" name="password"><br>
        爱好:<input type="checkbox" name="hobby" value="game">游戏
        <input type="checkbox" name="hobby" value="study">学习<br>
        <input type="submit" value="注册">
    </form>
    
  • 请求转发:一种在服务器内部的资源跳转方式

    • 步骤:
      • 通过request对象获取请求转发器对象 : RequestDispatcher getRequestDispatcher(String path)
      • 使用RequestDispatcher对象进行转发:void forward(ServletRequest req, ServletResponse resp)
    • 特点:
      • 浏览器地址栏路径不发生变化
      • 只能转发到当前服务器内部资源中
      • 转发是一次请求
        代码展示:
    // 请求路径 为 http://localhost:8081/demo/requestServletDemo7
    @WebServlet("/requestServletDemo7")
    public class RequestServletDemo7 extends HttpServlet {
    	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("demo777777被访问了。。。。。。。");
    //        RequestDispatcher requestDispatcher = request.getRequestDispatcher("/requestServletDemo8");
    //        requestDispatcher.forward(request,response);
    
            // 错误写法
    //        request.getRequestDispatcher("http://www.baidu.com").forward(request,response);
    
            request.getRequestDispatcher("/requestServletDemo8").forward(request,response);
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
        }
    }
    
    @WebServlet("/requestServletDemo8")
    public class RequestServletDemo8 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("demo888888被访问了。。。。。。。");
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
        }
    }
    
  • 共享数据

    • 域对象:一个有作用范围的对象,可以在范围内共享数据
    • request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据
    • 方法:
      • void setAttribute(String name, Object value):存储数据
      • Object getAttribute(String name):通过键获取数据
      • void removeAttribute(String name):通过键移除数据
        代码展示:
    // 请求路径 为 http://localhost:8081/demo/requestServletDemo7
    @WebServlet("/requestServletDemo7")
    public class RequestServletDemo7 extends HttpServlet {
    	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("demo777777被访问了。。。。。。。");
    		request.setAttribute("msg","hello!");
            request.getRequestDispatcher("/requestServletDemo8").forward(request,response);
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
        }
    }
    
    @WebServlet("/requestServletDemo8")
    public class RequestServletDemo8 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        	Object msg = request.getAttribute("msg");
        	System.out.println(msg);
            System.out.println("demo888888被访问了。。。。。。。");
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
        }
    }
    
  • 获取ServletContext

    • ServletContext getServletContext()

8.3 Response

HTTP/1.1 200
Content-Type: text/html;charset=utf-8
Content-Length: 30
Date: Tue, 17 Jan 2023 12:44:25 GMT
Keep-Alive: timeout=20
Connection: keep-alive

<h1>hello response,你好</h1>

数据格式:

  • 响应行:
    格式:协议/版本 响应状态码 状态码描述
    响应状态码:服务器告诉客户端浏览器本次请求和响应的一个状态。

    响应码都是3位数字
    分类:
    1XX: 服务器接收客户端消息,但没有接收完成,等待一段事件后,发送1XX状态码
    2XX: 成功。代表:200
    3XX: 重定向。代表:302(重定向),304(访问缓存)
    4XX: 客户端错误。代表:404(请求路径没有对应的资源),405(请求方式没有对应的doXxx方法)
    5XX: 服务器端错误。代表:500(服务器内部出现异常)

  • 响应头:
    格式:头名称:值
    常见的响应头:

    Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式
    Content-disposition: 服务器告诉客户端以什么格式打开响应体数据。
    值:1. in-line:默认值,在当前页面内打开
    2. attchment;filename=xxx: 以附件形式打开响应体。文件下载。

  • 响应空行:

  • 响应体:传输的数据

Response对象
功能:设置响应消息

  1. 设置响应行
    • 格式:HTTP/1.1 200 OK
    • void setStatus(int var1):设置状态码
  2. 设置响应头
    • void setHeader(String name, String value)
  3. 设置响应体
    • 步骤:
    • 获取输出流
      • 字符输出流:PrintWriter getWriter()
      • 字节输出流:ServletOutputStream getOutputStream()
    • 使用输出流,将数据输出到客户端浏览器

案例:

(1) 完成重定向

  • 重定向:资源跳转的方式

  • 简图展示:
    重定向

  • 代码实现

    @WebServlet("/responseServletDemo1")
    public class ResponseServletDemo1 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("ResponseServletDemo1.......");
            // 访问responseServletDemo1, 会自动跳转到/responseServletDemo2资源
            // 1. 设置状态码为302
    //        response.setStatus(302);
            // 2. 设置响应头location
    //        response.setHeader("location","/demo3/responseServletDemo2");
    
            // 简化
            response.sendRedirect("/demo3/responseServletDemo2");
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
        }
    }
    @WebServlet("/responseServletDemo2")
    public class ResponseServletDemo2 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("ResponseServletDemo2.......");
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
        }
    }
    
  • 重定向的特点,(与转发相反)

    • 地址栏发送变化
    • 重定向可以访问其他站点(服务器)的资源
    • 重定向是两次请求。不能使用request对象共享数据。
  • 路径的写法:

    • 路径分类
    • 相对路径:通过相对路径不可以确定唯一资源
      • 如: ./index.html
      • 不以/开头,以. 开头的路径
      • 规则:找到当前资源和目标资源之间的相对位置关系
        • ./ : 当前目录
        • ../ : 后退一级目录
    • 绝对路径:通过绝对路径可以确定唯一资源
      • 如:http://localhost/demo3/responseServletDemo1
      • /开头的路径
      • 规则:判断定义的路径是给谁用?(判断请求将来从哪儿发出)
        • 给客户端浏览器使用,需要加虚拟目录(项目的访问路径),因为存在多个相同的名字,需要在前面限定一下
          • 建议虚拟目录动态获取,request.getContextPath()
          • 使用场景:a标签,form表单标签,重定向
        • 给服务器使用,不需要加虚拟目录
          • 使用场景:转发路径

(2) 服务器输出字符数据到浏览器

  • 步骤:
    1. 获取字符输出流
    2. 输出数据

问题:中文乱码
原因:编码解码使用的字符集不一致,浏览器的字符集默认GBK(gb2312),而服务器端response.getWriter(),获取流的编码默认使用ISO-8859-1

  • 代码展示:
    @WebServlet("/responseServletDemo3")
    public class ResponseServletDemo3 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 解决中文乱码问题:
            // 方案1:在获取流对象之前,设置流的默认编码ISO-8859-1 -> GBK(知道浏览器以什么字符集解码时才能用)
    //        response.setCharacterEncoding("GBK");
    
            // 方案2:
    //        response.setCharacterEncoding("utf-8");
            // 告诉浏览器,服务器发送的消息体数据的编码,建议浏览器使用该编码解码
    //        response.setHeader("content-type","text/html;charset=utf-8");
    
            // 简化方案2:
            response.setContentType("text/html;charset=utf-8");
    
            // 1. 获取字符输出流
            PrintWriter pw = response.getWriter();
            // 2. 输出数据
            pw.write("<h1>hello response,你好</h1>");
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
        }
    }
    

(3) 服务器输出字节数据到浏览器

  • 步骤:
    1. 获取字节输出流
    2. 输出数据
  • 代码展示:
    @WebServlet("/responseServletDemo4")
    public class ResponseServletDemo4 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            response.setContentType("text/html;charset=utf-8");
            ServletOutputStream outputStream = response.getOutputStream();
            outputStream.write("hi,你好".getBytes("utf-8"));
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
        }
    }
    

(4) 验证码

  • 目的:防止恶意表单注册
  • 代码展示:
    @WebServlet("/checkCodeServlet")
    public class CheckCodeServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            int width = 100;
            int height = 50;
    
            // 1. 创建 一对象,在内存中图片
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
            // 2. 美化图片
            // 2.1 填充背景色
            Graphics graphics = image.getGraphics(); // 画笔对象
            graphics.setColor(Color.PINK);  // 设置画笔颜色
            graphics.fillRect(0,0,width,height);
    
            // 2.2 画边框
            graphics.setColor(Color.BLUE);
            graphics.drawRect(0,0,width-1,height-1);
    
            // 2.3 写验证码
            String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
            Random random = new Random();
            for (int i = 1; i <=4 ; i++) {
                int index = random.nextInt(str.length());
                // 获取字符
                char ch = str.charAt(index);
                graphics.drawString(ch+"",width/5*i,height/2);
            }
    
            // 2.4 画干扰线
            graphics.setColor(Color.GREEN);
            // 随机生成坐标点
            for (int i = 0; i <10 ; i++) {
                int x1 = random.nextInt(width);
                int x2 = random.nextInt(width);
                int y1 = random.nextInt(height);
                int y2 = random.nextInt(height);
                graphics.drawLine(x1,y1,x2,y2);
            }
    
            // 3. 将图片输出到页面展示
            ImageIO.write(image,"jpg",response.getOutputStream());
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
        }
    }
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>验证码</title>
        <script>
            /**
             * 分析:
             * 点击超链接或图片,需要换一张
             * 1. 给超链接或图片绑定单击事件
             * 2. 重新设置图片的src属性值
             */
            window.onload = function () {
                // 获取图片对象
                var img = document.getElementById("checkCode");
                // 绑定单机事件
                img.onclick = function change() {
                    // 加时间戳,保证点击获取不一样的验证码
                    var date = new Date().getTime()
                    console.log("date:", date)
                    img.src = "/demo3/checkCodeServlet?" + date
                }
            }
        </script>
    </head>
    <body>
    <img id="checkCode" src="/demo3/checkCodeServlet" alt="">
    <a id="change" href="javascript:;"
       onclick="document.getElementById('checkCode').src ='/demo3/checkCodeServlet?dt='+(new Date()).getTime()">看不清,换一张</a>
    </body>
    </html>
    

8.4 ServletContext

  • 概念:代表整个web应用,可以和程序的容器(服务器)来通信
  • 获取ServletContext对象
    • 通过request对象获取 request.getServletContext()
    • 通过HttpServlet获取 this.getServletContext()
  • 功能
    • 获取MIME类型
      • MIME 类型: 在互联网通信过程中定义的一种文件数据类型
        • 格式: 大类型/小类型 text/html,image/jpeg…
      • 获取 :String getMimeType(String file)
      • 代码展示
    @WebServlet("/servletContextDemo2")
    public class ServletContextDemo2 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            ServletContext context = this.getServletContext();
            String filename = "a.jpg";
            String mimeType = context.getMimeType(filename);
            System.out.println(mimeType);  // image/jpeg
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
        }
    }
    
    • 域对象:共享数据

      • setAttribute(String name,Object value)
      • getAttribute(String name)
      • removeAttribute(String name)

      范围:所有用户请求的数据

    • 获取文件的真实(服务器)路径

      • 代码展示:
    @WebServlet("/servletContextDemo5")
    public class ServletContextDemo5 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            ServletContext context = this.getServletContext();
            String bPath = context.getRealPath("/b.txt");
            System.out.println(bPath);  // E:\Workspaces\IdeaWorkspace\java_web_project\out\artifacts\servlet_demo_3_war_exploded\b.txt
            String cPath = context.getRealPath("/WEB-INF/c.txt");
            System.out.println(cPath);  // E:\Workspaces\IdeaWorkspace\java_web_project\out\artifacts\servlet_demo_3_war_exploded\WEB-INF\c.txt
            String aPath = context.getRealPath("/WEB-INF/classes/a.txt");
            System.out.println(aPath);  // E:\Workspaces\IdeaWorkspace\java_web_project\out\artifacts\servlet_demo_3_war_exploded\WEB-INF\classes\a.txt
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
        }
    }
    

综合案例:文件下载:

需求

  • 页面显示超链接
  • 点击超链接后弹出下载提示框
  • 完成图片文件下载

分析

  • 超链接指向的资源如果能被浏览器解析,则在浏览器中展示,如果不能解析,则弹出下载提示框。不满足需求
  • 任何资源都必须弹出下载提示框
  • 使用响应头设置资源的打开方式:
    • content-disposition:attachment;filename=xxx

步骤

  1. 定义页面,编辑超链接href属性,指向Servlet,传递资源名称filename
  2. 定义Servlet
    • 获取文件名称
    • 使用字符输入流加载进内存
    • 指定response的响应头:content-disposition:attachment;filename=xxx
    • 将数据写出到response输出流

注意问题:中文文件名问题
解决思路:

  1. 获取客户端使用的浏览器版本信息
  2. 根据不同的版本信息,设置filename的编码方式不同

代码展示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文件下载</title>
</head>
<body>
<p>浏览器默认解析,如果不能解析,则下载</p>
<a href="/demo4/img/1.jpg">图片</a>
<a href="/demo4/img/1.mp3">音乐</a>
<hr>
<p>不管可否解析,都以文件下载的形式,打开</p>
<a href="/demo4/downLoadServlet?filename=1.jpg">图片2</a>
<a href="/demo4/downLoadServlet?filename=1.mp3">音乐2</a>
<hr>
<p>中文问题</p>
<a href="/demo4/downLoadServlet?filename=图片.jpg">图片3</a>
</body>
</html>
@WebServlet("/downLoadServlet")
public class DownLoadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 获取请求参数
        String filename = request.getParameter("filename");
        // 2. 使用字节输入流加载进内存
        // 2.1 找到文件服务器路径
        ServletContext context = this.getServletContext();
        String realPath = context.getRealPath("/img/" + filename);
        // 2.2 用字节流关联
        FileInputStream fis = new FileInputStream(realPath);

        // 3. 设置response响应头
        // 3.1 设置响应头类型:content-type
        String mimeType = context.getMimeType(filename);  // 获取mime类型
        response.setContentType(mimeType);
        // 3.2 设置响应头打开方式:content-disposition

        // 解决中文文件名问题
        //   1. 获取user-agent请求头
        String agent = request.getHeader("user-agent");
        //   2. 使用工具类方法编码文件名即可
        filename = DownLoadUtils.getFileName(agent,filename);
        response.setHeader("content-disposition","attachment;filename="+filename);
        // 4. 将输入流的数据写出到输出流中
        ServletOutputStream sos = response.getOutputStream();
        byte[] bytes = new byte[1024 * 8];
        int len =0;
        while((len= fis.read(bytes))!=-1){
            sos.write(bytes,0,len);
        }
        fis.close();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/172105.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用sdk-npi-enablement-tool生成SVD文件和芯片头文件

使用sdk-npi-enablement-tool生成SVD文件和芯片头文件 文章目录使用sdk-npi-enablement-tool生成SVD文件和芯片头文件IntroductionOverviewOperation Steps创建芯片配置文件yaml填充外设模块的寄存器映射描述文件xlsx验证生成芯片头文件ConclusionIntroduction 芯片验证与测试…

【Linux杂篇】Linux系统终端常用配置文件更改

目录列表&#xff1a; 1.alias别名永久保存 2.解决vim文件没有颜色的问题 3.vim插件supertap插件安装&#xff08;可支持自动补全&#xff0c;非函数代码补全&#xff0c;仅支持在当前编辑文档内补全&#xff09; 4.vim插件管理 5.YCM下载 6.解决vim中使用backspace无法删…

windows安装npm和cnpm

npm: 代码的包管理器&#xff0c;但是服服器在国外&#xff0c;每一次启动项目都要下载一些依赖&#xff0c;耗时之久&#xff0c;官网下载链接戳 npm。 cnpm&#xff1a;这是淘宝团队出的npm的镜像&#xff0c;可用此代替官方的只读版本&#xff0c;官网链接 cnpm。 先安装np…

Redis6学习笔记【part3】配置文件与订阅/发布

一.Redis配置文件 1.Units单位 配置大小单位,开头定义了一些基本的度量单位&#xff0c;只支持bytes&#xff0c;不支持bit。其中 GB、Gb 大小写不敏感。 2.Include包含 类似 jsp 中的 include 引入公共页面 &#xff0c;redis 在多实例的情况也可以把公用的配置文件提取出来…

9 大指标分析 Solana 的熊市危机

Daniel, 2023 年 1 月Solana 是一个去中心化的区块链网络&#xff0c;由 Solana 实验室设计并在2020年推出&#xff0c;具有快速、可扩展和安全的特点。由于其快速的交易速度和低交易费用&#xff0c;Solana 在 2020 年和 2021 年获得了极大的关注&#xff0c;这使得它对去中心…

远程仓库操作

添加远程仓库 新建一个文件夹&#xff1a; 文件夹右键打开git bash: 初始化为git仓库&#xff1a; 在码云上新建一个git仓库&#xff1a; 复制链接&#xff1a; 在文件夹里使用git bash&#xff1a; git remote add<shortname><url> 添加一个新的远程仓库&…

制作tomcat的docker镜像

环境信息&#xff1a;MacBook Pro&#xff08;M1&#xff09;VMware-fusion(Player 版本 13.0.0 (20802013))Ubuntu 22.10tomcat镜像&#xff1a;centos-7.9.2009jdk1.8.0_341 apache-tomcat-8.5.84制作步骤&#xff1a;&#xff08;1&#xff09;下载好tomcat/jdk(我是在macbo…

学习react

这里写自定义目录标题学习React学习React 安装react的脚手架 npm i create-react-app -g通过脚手架创建demo D:\dev_project>create-react-app react-demo You are running Node 11.1.0. Create React App requires Node 14 or higher. Please update your version of No…

找不到合适好用的redis客户端工具?试试官方的客户端工具RedisInsight

这里是weihubeats,觉得文章不错可以关注公众号小奏技术&#xff0c;文章首发。拒绝营销号&#xff0c;拒绝标题党 背景 之前使用的redis客户端工具是AnotherRedisDesktopManager AnotherRedisDesktopManager github地址: https://github.com/qishibo/AnotherRedisDesktopManag…

千锋JavaScript学习笔记

千锋JavaScript学习笔记 文章目录千锋JavaScript学习笔记写在前面1. JS基础1.1 变量1.2 数据类型1.3 数据类型转换1.4 运算符1.5 条件1.6 循环1.7 函数1.8 对象数据类型1.9 数组和排序1.10 数组常用方法&#xff1a;1.11 字符串常用方法1.12 数字常用方法1.13 时间常用方法1.14…

九龙证券|三元锂离子电池和磷酸铁锂离子电池的特点和优劣势详解

动力蓄电池包括锂离子动力蓄电池、金属氢化物/镍动力蓄电池等。锂离子动力蓄电池一般简称为锂离子电池&#xff0c;锂离子电池是新能源轿车动力锂电池的重要品类&#xff0c;商场占有量也是最大的。新能源轿车商场上&#xff0c;锂离子电池常见的是磷酸铁锂离子电池和三元锂离子…

《图解HTTP》读书笔记

第一章、了解Web及网络基础 HTTP&#xff1a;HyperText Transfer Protocol&#xff0c;超文本传输协议。 HTML&#xff1a;HyperText Markup Language&#xff0c;超文本标记语言。 URL&#xff1a;Uniform Resource Locator&#xff0c;统一资源定位符。 把与互联网相关联的…

SQL题面试题

有3个表S(学生表)&#xff0c;C&#xff08;课程表&#xff09;&#xff0c;SC&#xff08;学生选课表&#xff09; S&#xff08;SNO&#xff0c;SNAME&#xff09;代表&#xff08;学号&#xff0c;姓名&#xff09; C&#xff08;CNO&#xff0c;CNAME&#xff0c;CTEACHER&…

SQL同时在线问题的解法

前言 同时在线相关的问题&#xff0c;在很多行业中也经常出现&#xff0c;比如&#xff1a; 统计同时最大主播数量统计同时最大在线人数统计同时最大打车人数… 很多人看到这类题&#xff0c;一脸懵逼&#xff0c;甚至连题意都看不懂&#xff0c;但是这道题是面试题中的常客&a…

SAP FICO CO-PA(获利分析会计)简介

一、概念信息 1、目标 CO-PA的目标&#xff1a;确定市场段的获利能力。 其中市场段就是一些指标的组合&#xff0c;比如客户A&#xff0c;加上产品&#xff11;就可以是一个获利分析段。 分析各个获利分析段的贡献边际&#xff0c;为后续销售决策提供支持&#xff08;主要与…

4、PyCharm中配置PyQt5-tools

1、 配置Qt Designer Workint directory&#xff1a;$FileDir$ 2、配置PyUIC Arguments&#xff1a;$FileName$ -o $FileNameWithoutExtension$.py Workint directory&#xff1a;$FileDir$ 3、配置PyRcc Arguments&#xff1a;$FileName$ -o $FileNameWithoutExtension$_rc.…

Linux常用命令——time命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) time 统计给定命令所花费的总时间 补充说明 time命令用于统计给定命令所花费的总时间。 语法 time(参数)参数 指令&#xff1a;指定需要运行的额指令及其参数。 实例 当测试一个程序或比较不同算法时&…

【进阶】Spring Boot配置文件(.properties提示有问题版)

努力经营当下&#xff0c;直至未来明朗&#xff01; 文章目录一、配置文件作用二、配置文件的格式三、properties配置文件说明1. properties 基本语法2. 读取配置文件3. properties缺点分析四、 yml配置文件说明1. yml基本语法2. yml使用进阶3. 注意&#xff1a;value值加单双引…

即时通讯开发之TCP 交互数据流、成块数据流

目前建立在 TCP 协议上的网络协议特别多,有 telnet,ssh,有 ftp,有 http 等等。 这些协议又可以根据数据吞吐量来大致分成两大类: 交互数据类型&#xff1a;例如 telnet,ssh,这种类型的协议在大多数情况下只是做小流量的数据交换,比如说按一下键盘,回显一些文 字等等。 数据…

【JavaScript】常见的事件(鼠标、键盘、表单等)

&#x1f4bb;【JavaScript】常见的事件 &#x1f3e0;专栏&#xff1a;JavaScript &#x1f440;个人主页&#xff1a;繁星学编程&#x1f341; &#x1f9d1;个人简介&#xff1a;一个不断提高自我的平凡人&#x1f680; &#x1f50a;分享方向&#xff1a;目前主攻前端&…