WEB阶段3:Response响应组成&常见状态码&ServletContext&url编码&文件下载案例

Response响应组成&常见状态码&ServletContext&url编码&文件下载案例

回顾

请求有哪三个的组成部分

  1. 请求行:提交方式 URI HTTP/1.1
  2. 请求头:由多个键和值组成
  3. 请求体:发送给服务器的数据,只有POST才有请求体

获取请求行相关的方法

HttpServletRequest对象的方法功能描述
String getMethod()获取提交方式
String getRequestURI()获取URI
String getProtocol()获取协议和版本
String getQueryString()获取查询字符串,?后面的参数

获取请求头相关的方法

请求方法功能描述
String getHeader(String headName)通过键获取值
Enumeration<String> getHeaderNames()获取所有的请求头的名字

获取请求参数的方法

方法名描述
String getParameter(String name)通过参数名获取参数值
String[] getParameterValues(String name)通过一个参数名获取所有同名的值,返回数组
Map<String,String[]> getParameterMap()一次性获取所有的参数,封装成map
键是参数名,值是参数值
POST方式乱码解决方法说明
setCharacterEncoding(“编码”)解决POST方法的乱码问题

请求域有关的方法

request与域有关的方法作用
Object getAttribute(“键”)通过键获取值
setAttribute(“键”,Object数据)向请求域中添加键和值,如果键不存在就是添加,存在就是修改
removeAttribute(“键”)通过键删除键和值

转发与重定向的区别

区别转发重定向
地址栏会不会发生变化不会
跳转方是哪一方服务器端浏览器端
请求域中数据是否丢失不会
根目录地址http://localhost:8080/项目访问地址/http://localhost:8080/
Servlet是运行在服务器端的,服务器端的 / 根目录是指的web目录,web目录在浏览器上访问的时候地址:
服务器端是包含了项目的访问地址:http://localhost:8080/项目访问地址/ 

网页是运行在浏览器端,浏览器端的 / 根目录是不包含项目名字的
http://localhost:8080/

学习目标

  1. 响应的格式
    1. 能够使用使用浏览器开发工具查看响应
    2. 能够理解响应行(状态行)的内容
    3. 能够理解常见的状态码
  2. 响应对象的编程
    1. 能够使用Response对象操作HTTP响应内容
    2. 能够处理响应乱码
    3. 能够完成文件下载案例
  3. 能够使用ServletContext域对象

学习内容

1. 响应的三个组成部分

目标

HTTP响应由哪三个组成部分

什么是HTTP响应

由服务器向浏览器发送的数据

数据准备

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
</head>
<body>
<h2>用户登录</h2>
<form action="login" method="post">
    <table>
        <tr>
            <td>用户名</td>
            <td><input type="text" name="username"/></td>
        </tr>
        <tr>
            <td>密码</td>
            <td><input type="password" name="password"/></td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="登录"/></td>
        </tr>
    </table>
</form>
</body>
</html>

响应信息的三个组成

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b0uTC9vY-1598791362482)(assets/image-20200830090313984.png)]

image-20200830090421486

小结

响应有哪三个组成部分?

  1. 响应行(状态码)
  2. 响应头:由键和值组成
  3. 响应体:服务器返回的数据,如果是网页看到的就是源码

2. 响应行、响应头、响应体的格式

目标

  1. 响应行的格式
  2. 响应头的格式
  3. 响应体的格式

响应行

组成:协议和版本 状态码 描述信息

HTTP/1.1 200 OK
HTTP/1.1 302 Found
HTTP/1.1 404 Not Found

响应头

响应头信息说明
Location: http://www.itcast.cn出现在302中,表示下一个要跳转的页面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DD1GdJ6I-1598791362499)(assets/image-20200830091153748.png)]
Server:Apache Tomcat服务器的名字
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zwbdFq1P-1598791362507)(assets/image-20200830091331777.png)]
Content-Encoding: gzip服务器端数据压缩的格式,浏览器需要知道压缩格式从而进行解压
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OS2Q7885-1598791362566)(assets/image-20200830091634611.png)]
Content-Length: 80服务器返回的数据长度,单位是字节
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-59p8XLZN-1598791362569)(assets/image-20200830091744979.png)]
Content-Type: text/html; charset=utf-8响应的类型和编码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V6M5Z2Bf-1598791362571)(assets/image-20200830091517641.png)]
Refresh:秒;url=/hello.html过多久以后跳转到另一个页面
Content-Disposition: attachment; filename=文件名.扩展名如果要实现文件的下载,必须指定这个响应头

响应体

有两种类型的数据

  1. 文本类型:网页,JS代码,CSS样式等

  2. 二进制类型:文件,图片,视频,音乐等

在Servlet中有相应的IO流:

  1. 文本类型:字符流,基类:Reader和Writer
  2. 二进制类型:字节流,基类:InputStream和OutputStream

小结:响应的组成

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kvf7o1XC-1598791362574)(assets/1552645116046.png)]

3. 响应对象的方法:与状态码有关(了解)

目标

与响应行有关的方法

什么是HttpServletResponse对象

是一个接口,也就可以有不同的实现类,我们的程序就可以运行在不同的Web服务器上。

  1. 由谁提供实现类:由tomcat实现
  2. 由谁创建此对象:由tomcat创建这个对象

设置状态码的方法

HTTP/1.1 200 OK
状态码的方法描述
setStatus(int status)设置响应的状态码,比如:200
sendError(int sc)发送错误码,通常400~500以上都属于错误码
sendError(int sc, String msg)发送错误码,多了一个错误信息

演示代码

package com.itheima.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("/demo1")
public class Demo1ResponseLineServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //response.setStatus(203);  //直接写成状态码
        //也可以使用常量
        //response.setStatus(HttpServletResponse.SC_ACCEPTED);  //200

        //发送错误码
        //response.sendError(401);  //会出现错误页面
        response.sendError(401, "你吃早餐了吗?");  //会出现错误页面
    }

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

小结

状态码的方法描述
setStatus(int status)设置状态码
sendError(int sc)发送错误码,可以带信息

4. 常见的状态码【重点】

目标

  1. 响应行中常见状态码的含义
  2. 404与405出现的原因

常见状态码的含义

200

表示服务器正常响应

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S5dnf4pp-1598791362592)(assets/image-20200830100225791.png)]

302

表示页面在浏览器端进行了页面的跳转

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OPMwBmYT-1598791362594)(assets/image-20200830100303244.png)]

304

静态页面使用了缓存

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x9XUf2pa-1598791362596)(assets/image-20200830100331603.png)]

404
  1. 地址栏输入错误,注:在Tomcat是区分大小写的。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oMyRZTh5-1598791362598)(assets/image-20200830100419834.png)]

  2. WEB-INF

    不能直接访问WEB-INF目录下的资源,如果访问也会出现404错误

    image-20200830100641955
  3. 重定向或转发地址不正确

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0T9Jcrm6-1598791362600)(assets/image-20200830100820032.png)]

    //request.getContextPath()的作用:获取当前项目的访问地址
    System.out.println(request.getContextPath());  //地址:/day27_02_login_war_exploded
    response.sendRedirect(request.getContextPath() + "/failure.html");
    
405
  1. 如果使用GET方法提交,Servlet中没有doGet方法

    image-20200830101345272
  2. 如果使用POST方法提交,Servlet中没有doPost方法

    image-20200830101425790

如果直接在浏览器上输入访问地址,使用的是GET方法,只有一种情况是POST,就是表单提交的时候method=“POST”

500

服务器出现异常,通常是你的代码有问题

image-20200830101636814

小结

状态码含义
200服务器正常的响应
302页面在浏览器端进行了页面跳转
304静态页面进行了缓存
404找不到资源
405没有doGet或doPost方法
500服务器代码有错误

5. 响应对象:响应头相关方法

目标

  1. 学习响应头相关的方法
  2. 案例:过3秒跳转到其它页面

响应头的方法

响应头的方法描述
void setHeader(String name,
String value)
向浏览器设置指定的响应头,指定名字和值
void setContentType(String type)相当于这个方法:
setHeader(“Content-Type”,“text/html;charset=utf-8”)
因为这个响应头使用频繁,所以专门有一个方法
response.setContentType("text/html;charset=utf-8");

案例:设置响应头过3秒跳转

步骤
  1. 创建ResponseRefreshServlet
  2. 调用setHeader,设置消息头(“Refresh”,“3;url=http://www.itcast.cn”)
效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rdat1NTp-1598791362601)(assets/image-20200830102744446.png)]

代码
package com.itheima.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.io.PrintWriter;

@WebServlet("/demo2")
public class Demo2ResponseRefreshServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.print("过3秒跳转到传智播客的官网");

        //设置refresh响应头
        response.setHeader("refresh","3;url=http://www.itcast.cn");
    }

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

案例:使用location实现页面跳转

步骤
  1. 只设置location响应头
  2. 同时设置302状态码
  3. 使用重定向的方法跳转
效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MjvbSB3Z-1598791362604)(assets/image-20200830105759715.png)]

代码
package com.itheima.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("/demo4")
public class Demo4LocationServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        /*//设置响应头location进行页面跳转
        response.setHeader("location","login.html");
        //还要设置状态码为302
        response.setStatus(302);*/

        //使用重定向跳转,重定向本质上就是上面的2行代码
        response.sendRedirect("login.html");
    }

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

小结

响应头的方法描述
void setHeader(String name, String value)设置响应头的它的值
void setContentType(String type)设置一个特殊的响应头:content-type

6. 案例:响应头数据压缩

目标

在服务器端对数据压缩后在浏览器端显示出来

需求

使用数据压缩之后再从服务器传输数据到浏览器, 可以减少网络的传输量,提高网页的下载速度。

GZIPOutputStream类的方法

构造方法说明
GZIPOutputStream(OutputStream out)创建一个压缩字节输出流,参数是被压缩的输出流。
下面的案例中需要压缩响应的字节输出流
GZIPOutputStream类的方法说明
public void write(byte[] b)把字节数据输出,参数是要输出的字节数组
void finish()清空缓存,结束输出

步骤

  1. 如果需要对数据进行压缩,需要使用压缩流,将响应的输出流做为参数。

  2. 使用压缩流的write方法:首先会对数据进行压缩处理,然后调用传递进去OutputStream对象的write方法把压缩的数据写出去。

  3. 完成将压缩数据写入输出流的操作,如果没有调用结束的方法,则浏览器上看不到东西。

要点

要设置相应响应头信息,告诉浏览器目前内容是以压缩包的形式传输的,不然就会以附件的方式下载下来了。

response.setHeader("Content-Encoding","gzip")

响应头

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jal35jet-1598791362605)(assets/image-20200830104008095.png)]

代码

package com.itheima.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.io.OutputStream;
import java.util.zip.GZIPOutputStream;

@WebServlet("/demo3")
public class Demo3GzipServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置响应头,告诉浏览器,服务器端数据是经过了压缩,压缩格式是gzip
        response.setHeader("Content-Encoding","gzip");
        //创建一个比较大的字节数组
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 5000; i++) {
            sb.append("abcdefg29374slkjdfsef");
        }
        //在服务器上输出数据原来的长度
        System.out.println("长度是:" + sb.length());
        //获取响应的输出流
        OutputStream out = response.getOutputStream();
        //创建压缩输出流
        GZIPOutputStream outputStream = new GZIPOutputStream(out);
        //直接使用字节输出流输出
        //输出到浏览器上,将字符串转成一个字节数组输出
        outputStream.write(sb.toString().getBytes());
        //输出完毕要清空缓存
        outputStream.finish();
    }

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

小结

需要设置一个响应头:

response.setHeader("Content-Encoding","gzip");
GZIPOutputStream类的方法说明
public void write(byte[] b)将压缩后的字节写到浏览器上
void finish()清空缓存

7. 响应体:处理响应乱码的问题【重点】

目标

  1. 响应体数据的两种方式
  2. 处理汉字乱码的问题

响应体的两种数据

  1. 字符流:Writer
  2. 字节流:OutputStream
响应体的方法描述
OutputStream getOutputStream()获取字节输出流
PrintWriter getWriter()获取字符输出流,字符流用来输出文本

代码

package com.itheima.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.io.PrintWriter;

/**
 * 打印流
 */
@WebServlet("/demo5")
public class Demo5WriterServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //告诉浏览器,我服务器使用的是什么编码
        //response.setContentType("text/html;charset=utf-8");
        
        //纯文本的内容,标签是不起作用的
        response.setContentType("text/plain;charset=utf-8");
        /*
        1. 告诉浏览器,我服务器使用的是什么编码
        2. 设置了响应字符输出流的编码
        3. 设置响应内容类型
         */
        //字符流必须设置编码
        //设置响应的编码,请求对象也有相同的方法,但浏览器并不知道你用的是什么编码
        //response.setCharacterEncoding("utf-8");
        PrintWriter out = response.getWriter();
        out.print("<h1>你好,打印流</h1>");
    }

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

小结

content-type响应头的作用:

  1. 告诉浏览器,服务器使用的编码
  2. 设置了响应字符输出流的编码
  3. 设置了响应的内容类型

8. 如何得到ServletContext对象【重点】

目标

如何得到上下文对象

什么是ServletContext

概念:每个Web模块或工程都会有一个对应的上下文对象,它们之间是一对一的关系。

实现以下三个功能:

  1. 读取web.xml中全局的配置参数
  2. 获取当前工程的资源,转成字节输入流对象
  3. 获取资源在服务器上真实地址
1552648163439

得到上下文对象(上下文域)的方法

ServletConfig接口中方法描述
ServletContext getServletContext()调用这个方法就可以获取上下文对象,
我们的Servlet中已经继承了这个方法
所以直接在类中调用就可以了。

回顾Servlet的继承结构

1552648301076

创建模块没有勾选web

  1. 在创建好的模块上点右键

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5CJ31WL9-1598791362607)(assets/image-20200830113230288.png)]

  2. 勾选web模块

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H2sBmPlS-1598791362608)(assets/image-20200830113256543.png)]

  3. 这时tomcat并没有自动部署,要手动去部署

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1FHHxeDC-1598791362609)(assets/image-20200830113328662.png)]

得到ServletContext的代码

package com.itheima.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("/demo1")
public class Demo1ServletContextServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取上下文对象
        ServletContext application = getServletContext();
        //输出到服务器上: org.apache.catalina.core.ApplicationContextFacade@48086280
        //它是一个接口,实现类由Tomcat
        //public class ApplicationContextFacade implements ServletContext 
        System.out.println(application);
    }

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

小结

  1. 每个Web项目对应几个上下文对象?

    1个

  2. 如何得到ServletContext?

    getServletContext(),它由tomcat实现,由tomcat实例化对象

9. ServletContext:读取全局的配置参数【了解】

目标

获取全局的配置参数

ServletContext方法

与以前学的ServletConfig接口中方法是一样的,但ServletConfig接口中的方法只能获取当前Servlet初始化的值,其它Servlet是不能获取这个参数值的

需求

在web.xml中设置2个全局的参数,username=NewBoy, age=21 在不同的Servlet中去获取这2个值,并使用上面2个方法。

代码

web.xml

<?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使用 -->
    <context-param>
        <param-name>user</param-name>
        <param-value>NewBoy</param-value>
    </context-param>

    <context-param>
        <param-name>age</param-name>
        <param-value>21</param-value>
    </context-param>

    <servlet>
        <servlet-name>demo2</servlet-name>
        <servlet-class>com.itheima.servlet.Demo2InitParamServlet</servlet-class>
        <!--局部配置参数-->
        <init-param>
            <param-name>user</param-name>
            <param-value>Rose</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>demo2</servlet-name>
        <url-pattern>/demo2</url-pattern>
    </servlet-mapping>
</web-app>

servlet代码

package com.itheima.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;
import java.io.PrintWriter;
import java.util.Enumeration;

/**
 * 使用配置的方式
 */
public class Demo2InitParamServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        //使用ServletConfig的方法可以获取自己的参数,局部参数
        String user = getInitParameter("user");  //ServletConfig的方法
        out.print("局部初始参数:" + user + "<hr/>");

        //获取全局的配置参数
        ServletContext application = getServletContext();
        //调用上下文对象的方法
        String user1 = application.getInitParameter("user");  //ServletContext的方法
        out.print("全局的配置参数:" + user1 + "<hr/>");

        //获取所有的全局参数的名字
        Enumeration<String> initParameterNames = application.getInitParameterNames();
        while (initParameterNames.hasMoreElements()) {
            String name = initParameterNames.nextElement();  //获取一个参数名
            String value = application.getInitParameter(name);  //获取参数的值
            out.print("全局参数名:" + name + ",参数值:" + value + "<hr/>");
        }
    }

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

效果

image-20200830115205992

小结

方法功能
ServletContext getServletContext()获取上下文对象的方法
String getInitParameter(String name)通过参数名获取参数值,全局的初始参数
所有的Servlet都可以使用
Enumeration<String> getInitParameterNames()获取所有的参数名

10. ServletContext:获取当前工程的资源

目标

得到web目录下的某个图片资源在浏览器上显示出来

idea的bug

web目录下静态资源,如果是复制过去,idea不会自动部署,在out目录下没有,如果是自己新建的不会有问题。

解决方法

自己手动在out目录下再复制一份

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P3omkH3r-1598791362611)(assets/image-20200830144011571.png)]

案例:读取Web目录下的资源文件

执行效果
image-20200830145523598
代码
package com.itheima.servlet;

import org.apache.commons.io.IOUtils;

import javax.servlet.ServletContext;
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.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * application.getResourceAsStream("/") 表示web目录
 * 读取web目录或它的目录下资源,返回字节输入流
 */
@WebServlet("/demo3")
public class Demo3ResourceServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //回顾:获取src目录下资源,转成输入流
        //InputStream inputStream = getClass().getResourceAsStream("/xxx");
        //0.获取上下文对象
        ServletContext application = getServletContext();
        //1.获取一个输入流对象,这里的/是web目录
        InputStream inputStream = application.getResourceAsStream("/WEB-INF/img/404.jpg");
        //2.获取一个响应的输出流
        OutputStream outputStream = response.getOutputStream();
        //3.把输入流写到输出流中去
        /*int len = 0;
        byte[] buf = new byte[1024];
        while ((len = inputStream.read(buf)) != -1) {
            outputStream.write(buf, 0, len);
        }*/

        //使用工具IOUtils,用来操作IO流的静态方法
        IOUtils.copy(inputStream, outputStream);

        //4.响应会自动关闭流
        inputStream.close();
        outputStream.close();
    }

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

使用IOUtils工具类的方法功能
copy(InputStream in, OutputStream out)把输入流中的数据写到输出流中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WfomllS0-1598791362612)(assets/image-20200830145341211.png)]

小结

ServletContext的方法功能
InputStream getResourceAsStream(String path)读取web或它的子目录下资源,转成字节输入流

11. ServletContext:获取资源在服务器的真实地址

目标

使用上下文对象得到web资源真实的地址

代码

package com.itheima.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;
import java.io.PrintWriter;

/**
 * 获取一个资源在服务器部署的真实地址
 */
@WebServlet("/demo4")
public class Demo4RealPathServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取上下文对象
        ServletContext application = getServletContext();
        //2.调用方法:获取资源在服务器上部署的真实地址。参数:资源的路径名
        String realPath = application.getRealPath("/WEB-INF/img/404.jpg");

        //3.输出资源的真实地址
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();

        out.print("真实地址是:" + realPath);
    }

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

执行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uknx153Y-1598791362614)(assets/image-20200830150352672.png)]

小结

ServletContext的方法功能
String getRealPath(String path)获取指定资源部署到服务器上真实地址

12. 上下文域对象的方法

目标

  1. 上下文域的操作方法
  2. 得到第几个登录用户的分析

上下文域

作用域有三个:

  1. 请求域:一个用户的一次请求起作用,范围最小。
  2. 会话域:一个用户的所有请求。
  3. 上下文域:所有用户的所有请求。都是起作用的,范围是最大。

作用范围越大,占用资源越多,原则:在满足功能的前提下,使用尽可能小的作用域。

上下文域是服务器启动就存在,服务器关闭才销毁。

1552655112707

上下文域的操作方法

所有的作用域都有三个相同的方法

上下文域操作有关的方法作用
Object getAttribute(“键”)通过键获取上下文域中值
setAttribute(“键”,Object数据)向上下文域中添加键和值,如果存在就是覆盖
removeAttribute(“键”)从上下文域中删除指定的键和值

需求

记录当前是第几个登录成功的用户

案例流程

  1. 计数放在上下文域中
  2. 在Servlet每一次访问的时候,向上下文域中添加1个变量count=0
  3. 每个用户登录
  4. 登录成功:从上下文域中取出count的值,加1,更新上下文域
  5. 登录失败:输出登录失败
1552655394873

13. 案例:得到当前是第几个登录的用户

目标

得到当前是第几个登录的用户的代码实现

步骤

  1. 在init方法中得到上下文对象

  2. 创建count=0,并且将值放入上下文域中.

  3. 登录方法中得到用户名和密码

  4. 判断用户名和密码是否正确

  5. 如果正确,得到上下文对象

  6. 从上下文域中取出count

  7. 加1再更新上下文域

  8. 显示输出人数

  9. 登录失败则跳转到登录页面

代码

HTML
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
</head>
<body>
<h2>用户登录</h2>
<form action="login" method="post">
    <table>
        <tr>
            <td>用户名</td>
            <td><input type="text" name="username"/></td>
        </tr>
        <tr>
            <td>密码</td>
            <td><input type="password" name="password"/></td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="登录"/></td>
        </tr>
    </table>
</form>
</body>
</html>
登录的Servlet
package com.itheima.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;
import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 记录当前是第几个登录的用户
 */
@WebServlet("/login")
public class Demo5LoginServlet extends HttpServlet {
    
    /*
    第一个用户登录的时候执行1次
    注:
    因为每个Servlet只有一个对象,每个用户其实是一个线程,
    所以Servlet中成员变量同时给多个用户使用的会有线程安全的问题,建议不要对成员变量进行修改。
     */
    @Override
    public void init() throws ServletException {
        //获取上下文对象,向上下文域中添加了一个计数为0
        getServletContext().setAttribute("count", 0);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();

        //实现用户的登录
        String username = request.getParameter("username");  //获取用户名
        String password = request.getParameter("password");  //获取用户名
        //直接判断,登录成功
        if ("admin".equals(username) && "123".equals(password)) {
            //从上下文域中取出原有的值
            ServletContext application = getServletContext();
            //获取一个整数类型的值
            int count = (int) application.getAttribute("count");
            //计数加1,再放回到上下文域中
            application.setAttribute("count", ++count);
            //输出第几个登录的用户
            out.print("登录成功,您是第" + count + "个登录的用户");
        } else {
            out.print("登录失败,<a href='login.html'>请重试</a>");
        }
    }

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

小结

上下文域的范围是:所有用户的所有请求。服务器开启就会创建上下文域,服务器关闭才会销毁

ServletContext的方法作用
Object getAttribute(“键”)获取上下文域中值
void setAttribute(“键”,Object 数据)设置上下文域中键和值
void removeAttribute(“键”)删除上下文域中的键和值

14. URL编码和解码

目标

  1. 为什么需要URL编码和解码
  2. 如何实现URL编码和解码

为什么需要URL编码和解码

表单中提交汉字,使用GET提交,查看请求行的数据

URL的编码规则
字母数字字符 "a" 到 "z"、"A" 到 "Z" 和 "0" 到 "9" 保持不变。 
特殊字符 "."、"-"、"*" 和 "_" 保持不变。 
空格字符 " " 转换为一个加号 "+"。 
所有其他字符都是不安全的,因此首先使用一些编码机制将它们转换为一个或多个字节。然后每个字节用一个包含 3 个字符的字符串 "%xy" 表示,其中 xy 为该字节的两位十六进制表示形式。推荐的编码机制是 UTF-8。

如:张三 编码后:%E5%BC%A0%E4%B8%89

有关的方法

URL编码和解码有关的方法功能
URLEncoder.encode(“字符串”,“utf-8”)URL编码,参数1:要编码的字符串,
参数2:编码的类型,统一使用utf-8
URLDecoder.decode(“字符串”,“utf-8”)URL解码:参数2:要解码的字符串,
参数2:编码的类型,统一使用utf-8

案例演示代码

package com.itheima.test;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;

public class TestURL {

    public static void main(String[] args) throws UnsupportedEncodingException {
        String hz = "张三";
        //URL编码,参数1:要编码的字符串,参数2:编码的类型,统一使用utf-8
        String encode = URLEncoder.encode(hz, "utf-8");
        System.out.println("编码前:" + hz);
        System.out.println("编码后:" + encode);

        //URL解码:参数2:要解码的字符串,参数2:编码的类型,统一使用utf-8
        String decode = URLDecoder.decode(encode, "utf-8");
        System.out.println("解码后:" + decode);
    }
}

小结

URL编码和解码有关的方法功能
URLEncoder.encode(“字符串”,“utf-8”)URL的编码
URLDecoder.decode(“字符串”,“utf-8”)URL的解码

15. 案例:使用链接下载文件不足

目标

链接下载文件存在的问题

页面效果

 1552655687657

下载页面

<!DOCTYPE html>
<html>
<head>
    <title>资源下载列表</title>
    <meta charset="utf-8">
</head>

<body>
<h2>文件下载页面列表</h2>
<h3>超链接的下载</h3>
<!--直接链接下载的资源,download是一个目录 -->
<a href="download/file.txt">文本文件</a><br/>
<a href="download/file.jpg">图片文件</a><br/>
<a href="download/file.zip">压缩文件</a><br/>
<hr/>
<h3>手动编码的下载方式</h3>
<!-- 注:down这里是下载的Servlet的访问地址 -->
<a href="down?filename=file.txt">文本文件</a><br/>
<a href="down?filename=file.jpg">图片文件</a><br/>
<a href="down?filename=file.zip">压缩文件</a><br/>
<!--以后不建议使用汉字的文件名-->
<a href="down?filename=美女.jpg">美女</a><br/>
</body>
</html>

小结:使用超链接下载的不足

  1. 有些资源是在浏览器上直接打开,不是下载。
  2. 会暴露资源的真实地址,容易被人盗链
  3. 不利于业务逻辑的控制,比较如:要登录或是会员才可以下载

16. 案例:使用Servlet下载文件【重点】

目标

  1. 实现使用Servlet的方式下载文件
  2. 文件名有汉字的处理

下载设置的响应头

设置响应头参数说明
Content-Disposition: attachment; filename=文件名Content-Disposition:
1. inline表示在浏览器中打开,默认值
2. attachment: 以附件的方式下载资源
filename: 下载的文件名

步骤

  1. 从链接上得到文件名
  2. 设置content-disposition头
  3. 得到文件的输入流
  4. 得到response的输出流
  5. 写出到浏览器端

汉字乱码原理

image-20200830161955689

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gSak2te3-1598791362616)(assets/1552656145066.png)]

注:IE、Chrome下载中文采用的是URL编码,注:FireFox不能采用这种方式。

下载的Servlet代码

package com.itheima.servlet;

import org.apache.commons.io.IOUtils;

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.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;

@WebServlet("/down")
public class DownloadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取下载的文件名
        String filename = request.getParameter("filename");
        System.out.println("下载的文件名是:" + filename);
        //要点:如果要下载,必须设置响应头
        //因为浏览器会自动对汉字进行解码的操作,所以我们在服务器端对汉字进行编码
        response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename,"utf-8"));

        //2.使用上下文对象获取文件的输入流
        InputStream inputStream = getServletContext().getResourceAsStream("/download/" + filename);
        //3.获取响应的输出流
        OutputStream outputStream = response.getOutputStream();
        //4.将输入流复制到输出流中,注:要导入io工具的jar包到lib目录下
        IOUtils.copy(inputStream, outputStream);
        //5.关闭流
        inputStream.close();
        outputStream.close();
    }

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

小结

  1. 下载需要设置如个响应头?

    content-disposition:attachment;filename=文件名
    
  2. 如果下载文件名中有汉字使用哪个方法编码?

    URLEncoder.encode("字符串","utf-8")
    

17. 验证码的使用

目标

  1. 将上面创建的验证码用在HTML页面中
  2. 点击验证码图片刷新验证码的功能

 1552656787141

验证码的代码

package com.itheima.servlet;

import javax.imageio.ImageIO;
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.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

@WebServlet("/code")
public class PicCodeServlet extends HttpServlet {

    //随机类
    private Random random = new Random();

    /**
     * 随机获取一种颜色
     */
    private Color getRandomColor() {
        //随机得到r,g,b的取值,范围是0~255
        int r = random.nextInt(256);
        int g = random.nextInt(256);
        int b = random.nextInt(256);
        return new Color(r, g, b);  //red红 green绿 blue蓝
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置MIME类型
        response.setContentType("image/jpeg");
        //定义宽和高的值
        int width = 90;
        int height = 30;
        //1. 创建一张图片,参数:宽,高,图片模式
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        //获取画笔对象
        Graphics graphics = image.getGraphics();
        //整个图片填充白色
        graphics.setColor(Color.WHITE);
        graphics.fillRect(0,0,width,height);

        //2. 随机绘制4个验证码
        char[] arr = { 'A', 'B', 'C', 'D', 'N', 'E', 'W', 'b', 'o', 'y', '1', '2', '3', '4','5','6','7','8' };

        //设置字体,字体对象有三个参数:字体名字,字体样式(加粗,斜体), 大小
        graphics.setFont(new Font(Font.DIALOG_INPUT, Font.BOLD + Font.ITALIC, 19));

        for (int i = 0; i < 4; i++) {
            //随机获取1个索引号
            int index = random.nextInt(arr.length);
            //随机获取字符数组的一个字符
            char c = arr[index];
            //每个字符的颜色不同
            graphics.setColor(getRandomColor());
            //写字符,参数:文字内容,x,y坐标  (把字符转成字符串)
            graphics.drawString(String.valueOf(c),10+(i*20), 20);
        }

        //3. 绘制8条干扰线
        for (int i = 0; i < 8; i++) {
            //指定颜色
            graphics.setColor(getRandomColor());
            int x1 = random.nextInt(width);
            int y1 = random.nextInt(height);
            int x2 = random.nextInt(width);
            int y2 = random.nextInt(height);
            //画线,起点和终点
            graphics.drawLine(x1,y1,x2,y2);
        }

        //4. 把图片输出到浏览器,参数:输出的图片对象,图片格式,响应输出流
        ImageIO.write(image,"jpg", response.getOutputStream());
    }

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

代码

<!--验证码,src要指定为servlet的访问地址-->
<img src="code" id="picCode" style="width: 90px; height: 30px; cursor: pointer;" title="看不清,点击刷新">
<script type="text/javascript">
    //点击图片的事件
    document.getElementById("picCode").onclick = function () {
        //刷新页面  location.reload();
        //再次访问服务器,将src的值重新赋值,默认会使用缓存
        //每次传递不同的参数就可以修改URL地址了,参数并没有什么用
        this.src = "code?t=" + new Date().getTime();
    }
</script>

小结

实现点击验证码刷新的功能:每次传递不同的参数

学习总结

  1. 能够使用浏览器开发工具查看响应

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YGSZOHbl-1598791362618)(assets/1552645116046.png)]

  2. 能够理解响应行(状态行)的内容

    HTTP/1.1 200 OK

    1. 协议和版本
    2. 状态码
    3. 状态描述
  3. 能够理解常见的状态码

    状态码含义
    200正常的响应
    302浏览器端进行了跳转
    304使用了缓存
    404找不到资源
    405没有重写doGet或doPost方法
    500服务器代码有错误
  4. 能够使用Response对象操作HTTP响应内容

    响应头的方法描述
    setStatus(int status)设置状态码
    void setHeader(String name, String value)设置某个响应头
    OutputStream getOutputStream()获取字节输出流
    PrintWriter getWriter()获取字符输出流
  5. 能够处理响应乱码

    处理响应乱码的方法描述
    void setContentType(String type)1. 告诉浏览器,服务器端使用的编码
    2. 设置响应的编码,相当于下面这个方法
    3. 设置响应的内容类型
    response.setCharacterEncoding(“字符集”)设置响应的编码
  6. 能够完成文件下载案例

    1. 设置下载的响应头
    2. 通过上下文对象getResourceAsStream()得到输入流
    3. 获取响应的输出流
    4. IOUtils.copy(输入流,输出流)
    设置响应头参数说明
    Content-Disposition: attachment; filename=文件名设置浏览器以附件的方式下载文件
    而不是在浏览器中直接打开
  7. 能够使用ServletContext域对象

    ServletConfig接口中方法描述
    ServletContext getServletContext()获取上下文对象
    ServletContext的方法作用
    InputStream getResourceAsStream(String path)读取web目录下的资源文件,转成输入流
    String getRealPath(String path)读取web目录下资源的部署到服务器上的真实地址
    setAttribute() getAttribute() removeAttribute()
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值