响应请求

02.ServletContext作用1—读取当前项目中资源文件【应用】

目标

掌握使用ServletContext对象读取当前项目内的资源文件

ServletContext介绍
ServletContext是一个上下文对象,每个 Web 应用程序都对应了一个上下文对象。
用于 Servlet 与容器(服务器)之间进行通讯,可以通过这个对象得到与 Web 容器(服务器)相关信息。ServletContext实例是通过 getServletContext()方法获得的。
作用
1.servletContext可以读取项目内的资源文件
2.读取web.xml中全局配置参数数据
3.是一个域对象,全局共享域对象。所有动态资源都可以操作这个内存对象读写数据
api方法
1.String getServletContext().getRealPath(相对路径)
根据相对路径获取项目部署位置上资源的绝对路径

2.InputStream getServletContext().getResourceAsStream(相对路径);
根据项目资源相对路径获取部署资源文件的输入流
案例需求

使用servletContext读取项目内web/img/图片资源文件,并输出到浏览器显示

实现步骤
  1. 在项目中准备一个资源文件
  2. 创建一个Servlet通过ServletContext对象读取资源文件
  3. 动态方式向浏览器输出资源文件
实现代码

代码结构

[外链图片转存失败(img-inBMafdz-1566098107668)(assets/)] 

代码

package com.itheima.servletcontext._作用1读取项目内资源文件;

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

@WebServlet(name = "_01GetFileServlet_Path", urlPatterns = "/_01GetFileServlet_Path")
public class _01GetFileServlet_Path extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //目标:使用servletContext对象读取项目内的文件/img/5.jpg文件输出到浏览器显示

        //1.获取资源文件在部署位置的【绝对路径】
        ServletContext application = getServletContext();
        //根据“/img/图片文件名”相对路径获取的绝对路径
        String path = application.getRealPath("/img/5.jpg");

        //2.根据绝对路径创建文件输入流
        InputStream inputStream = new FileInputStream(path);


        //3.将输入流的数据写出到输出流,显示到浏览器
        //定义读取数据的字节数组
        byte[] bytes = new byte[1024];
        //定义读取的长度
        int length=-1;
        //获取浏览器字节输出流
        OutputStream outputStream = response.getOutputStream();
        //循环读取并写出到输出流
        while ((length=inputStream.read(bytes))!=-1){
            outputStream.write(bytes,0,length);
        }

        //关闭流
        inputStream.close();
        //outputStream 不用关,因为服务器会自动关闭
    }
}
运行

访问地址

http://localhost:8080/day26_servletcontext_response_war_exploded/_01GetFileServlet_Path

运行效果

[外链图片转存失败(img-A4srNWaD-1566098107670)(assets/)]

servletContext获取文件的输入流方式读取文件

代码结构

[外链图片转存失败(img-eMRhRhMd-1566098107670)(assets/)] 

实现代码

[外链图片转存失败(img-0I3iOyfA-1566098107671)(assets/)]

访问地址

http://localhost:8080/day26_servletcontext_response_war_exploded/_02GetFileServlet_Stream

运行效果

[外链图片转存失败(img-eZ6VSxD6-1566098107671)(assets/)]

疑问:以前学习根据类路径读取文件和现在ServletContext读取文件,以后使用哪个?

[外链图片转存失败(img-oCBwddPq-1566098107671)(assets/)] 

jdbc.properties在src目录下

读取src类路径下面的文件推荐使用类路径方式
读取web项目其他位置推荐使用ServletConext方式

使用类路径读取文件输入流
	方式一: Demo.class.getResourceAsStream("/jdbc.propertes")  //第一个/代表src目录
	方式二: getServletContext().getResourceAsStream("/WEB-INF/classes/jdbc.propertes");  //路径中的第一个/代表当前项目内
使用第三方工具类简化流读写操作

第三方工具包

[外链图片转存失败(img-qYE3Xb0e-1566098107672)(assets/)] 

工具类api方法

[外链图片转存失败(img-GL9YLjcl-1566098107672)(assets/1565978411690.png)]

实现步骤

1.导入第三方工具包commons-io-2.1.jar到项目中WEB-INF/lib目录下并引用
2.使用工具类方法实现将输入流数据自动写入到输出流显示

导入jar包

[外链图片转存失败(img-xM9uXS9V-1566098107673)(assets/1566007032154.png)] 

代码位置

[外链图片转存失败(img-7YOA8WkB-1566098107673)(assets/)] 

实现代码

[外链图片转存失败(img-LY151fRL-1566098107674)(assets/)]

运行效果与上面一样

小结
  • servletContext可以读取项目资源部署资源文件的真实路径的api方法?

    getServletContext().getRealPath(项目内的相对路径);
    
  • servletContext可以读取项目资源部署资源文件的输入流方法?

    getServletContext().getResourceAsStream(项目内的相对路径)
    

03.ServletContext作用2—读取全局参数【应用】

目标

掌握ServletContext对象读取全局参数

全局参数介绍

全局参数数据是在web.xml中配置的数据,所有servlet可以共享读取。设置为全局参数的好处是,可以在不修改代码的前提下更换配置数据。

<!--全局参数示例-->
<context-param>
	<param-name>country</param-name>
	<param-value>China</param-value>
</context-param>
应用场景

适合配置应用程序使用的统一码表,这样所有servlet可以读取进行应用。

使用原因:可以在不修改代码的前提下,只需要修改web.xml配置文件就可以更换统一的码表。

ServletContext对象读取全局参数api方法
String getInitParameter(java.lang.String name) 指定参数的名字,得到值。
Enumeration<String> getInitParameterNames() 得到所有的全局参数名
案例需求

读取全局参数演示,设置全局参数country=China,Charset=utf-8, 使用ServletContext对象读取全局参数

实现步骤
1.在web.xml配置全局参数
2.在servlet里面使用ServletContext对象读取全局参数并打印输出
实现代码

代码结构

[外链图片转存失败(img-AKiUxTBI-1566098107674)(assets/)] 

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">

    <!--设置全局参数country=China,charset=utf-8-->
    <context-param>
        <param-name>country</param-name>
        <param-value>China</param-value>
    </context-param>
    <context-param>
        <param-name>charset</param-name>
        <param-value>utf-8</param-value>
    </context-param>
</web-app>

Servlet代码

package com.itheima.servletcontext._作用1读取项目内资源文件;

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.Enumeration;

@WebServlet(name = "_04ReadDataGlobal", urlPatterns = "/_04ReadDataGlobal")
public class _04ReadDataGlobal extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //目标:读取所有的全局参数循环输出每一对参数值
        //1.读取全局参数列表
        Enumeration<String> enumeration = getServletContext().getInitParameterNames();
        //2.循环遍历
        while (enumeration.hasMoreElements()){
            //获取这个参数名
            String paramName = enumeration.nextElement();
            //根据参数名获取参数值
            String paramValue = getServletContext().getInitParameter(paramName);

            //打印输出
            System.out.println(paramName+"="+paramValue);

        }
    }
}
运行效果

方法地址

http://localhost:8080/day26_servletcontext_response_war_exploded/_04ReadDataGlobal

效果

[外链图片转存失败(img-a8GMtFei-1566098107674)(assets/g)]

04.ServletContext作用3—上下文域对象【应用】

目标

使用上下文域对象存储所有动态资源共享的数据

域对象的作用(内存对象)

用于实现动态资源之间传递数据

[外链图片转存失败(img-lnSDHSdC-1566098107675)(assets/)] 

上下文域对象介绍
从服务器开启到服务器关闭都是有效的,所有的用户所有的 Servlet 之间都可以实现数据共享。
作用域对象有三个:request请求域对象,会话域对象,上下文域对象(servletcontext)
凡是作用域对象都有3个方法如下

[外链图片转存失败(img-OAIMSa40-1566098107675)(assets/)]

应用场景
适合统计所有用户共享的数据,比如:统计登录的人数
案例需求

每个用户进行登录,登录后显示“第几个用户登录”

[外链图片转存失败(img-S6r5AA1K-1566098107676)(assets/)]

实现过程

[外链图片转存失败(img-pQgJ1lma-1566098107676)(assets/)]

实现步骤
1. 在 LoginServlet 的 init()方法中创建 count=0,并且将值放入上下文域中.
2. 在登录成功的代码中从上下文域中取出 count,并且加 1,再更新上下文域中的值。
3. 跳转到另一个 CountServlet,在另一个 CountServlet 中取出上下文域中的值,显示在页面上。

登录页面素材

<!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-TnkIacO4-1566098107676)(assets/)] 

LoginServlet代码

package com.itheima.servletcontext._作用3上下文域对象存储登录人数;

import javax.servlet.ServletConfig;
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(name = "LoginServlet", urlPatterns = "/login")
public class LoginServlet extends HttpServlet {

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);

        //初始化上下文域统计count = 0  代表登录人数为0
        getServletContext().setAttribute("count",0);
    }

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

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //标准动作
        //解决post乱码
        request.setCharacterEncoding("utf8");
        //解决response输出乱码
        response.setContentType("text/html;charset=utf8");

        //1.获取表单提交的用户名与密码
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        //2.登录校验,用户名必须为admin,密码必须为123
        if("admin".equals(username) && "123".equals(password)) {

            //3.校验成功
            //3.1 从上下文域取出count统计值
            int count = (int) getServletContext().getAttribute("count");

            //3.2 将count+1 之后更新回域
            //getServletContext().setAttribute("count",count++);  这样是先存后加
            getServletContext().setAttribute("count",++count);  //这样是先加后存

            //3.3 转发跳转到CountServlet,去显示登录人数
            request.getRequestDispatcher("/CountServlet").forward(request,response);
        }else {

            //4.校验失败,输出登录失败
            response.getWriter().write("登录失败");
        }
    }
}

CountServlet代码

package com.itheima.servletcontext._作用3上下文域对象存储登录人数;

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(name = "CountServlet", urlPatterns = "/CountServlet")
public class CountServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //1.从上下文域对象中获取统计登录人数
        int count = (int) getServletContext().getAttribute("count");

        //2.显示结果“您是第X位登录成功的用户!”
        response.getWriter().write("您是第"+count+"位登录成功的用户!");
    }
}

运行效果

访问地址

http://localhost:8080/day26_servletcontext_response_war_exploded/login.html

效果

[外链图片转存失败(img-vgl9rJBW-1566098107677)(assets/)]

小结
  • 请求说出目前学习过的域对象有哪些?

    request,请求域
    servletContext,上下文域
    
  • 这些域对象的区别?

    request,请求域, 一个客户端一次请求内
    servletContext,上下文域, 所有客户端所有请求都有效,全局共享
    
  • 响应数据有几部分组成?

  响应行,头,体

06.响应行-组成与状态码介绍【理解】

目标

理解响应行的数据组成

掌握常见6种通信状态码

响应行的数据组成
HTTP/1.1 200 OK

http协议版本

200,http协议状态码,描述服务器通信的状态

ok,状态码的翻译,代表通信正常, tomcat8服务器没有这个翻译, 之前的版本有

常见http状态码

[外链图片转存失败(img-9GKuHXKP-1566098555752)(assets/)]

404发生的情况

​ 访问的url写错了(几率最大)

​ 服务器没有这个资源

​ 编写响应行代码,设置错误代码输出404错误

response设置响应行状态码的方法

response对象介绍

所属类型HttpServletResponse接口
它是一个接口,没有具体的实现。由tomcat去实现,并且在运行的时候实例化,传递给Servlet的处理请求方法参数response

设置状态码方法

[外链图片转存失败(img-S27Otexu-1566098555754)(assets/)]

代码结构

[外链图片转存失败(img-VVQPVdes-1566098555757)(assets/)]

实现代码

​```java
package com.itheima.response._响应行;

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(name = “_01SetCodeServlet”, urlPatterns = “/_01SetCodeServlet”)
public class _01SetCodeServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    /*操作response设置状态码相关api方法*/

    //1.设置状态码方法,单独使用没有任何效果和含义,必须配合响应头设置才有效果
    //response.setStatus(302);

    //2.发送一个错误码
    //response.sendError(404);//浏览器就会显示404错误状态码

    //3.发送一个错误码和消息
    response.sendError(404,"大家饿了吗?");//浏览器就会显示404错误状态码和错误消息

}

}


运行访问地址

http://localhost:8080/day26_servletcontext_response_war_exploded/_01SetCodeServlet


##### 错误状态码405演示

需求

访问一个Servlet没有doGet,doPost处理请求方法会报405错误


实现步骤

 1. 创建一个Servlet没有doGet,doPost处理请求

 2. 浏览器发送get请求,由于服务器没有方法所以会报405错误


运行访问地址

http://localhost:8080/day26_servletcontext_response_war_exploded/_02Error405Servlet


运行效果



如果你的代码运行发生了这个错误,如何解决这个错误?

添加重写doGet或doPost方法




##### 错误状态码500演示

需求

访问一个Servlet处理请求会抛出异常,查看浏览器会报500错误


实现步骤

1. 创建一个Servlet,编写可以抛出异常的代码
2. 浏览器发送请求这个Servlet

运行访问地址

http://localhost:8080/day26_servletcontext_response_war_exploded/_03Error500Servlet



如果你的代码运行发生了这个错误,如何解决这个错误?

调试代码解决


> 备注:
>
> ​	500的错误99%是编写的代码存在异常问题导致的
>
> ​	1%外在因素影响,如下情况:
>
> ​			比如使用代码访问网络,电脑网络联不通;
>
> ​			代码访问数据库,数据库软件服务没有开启

##### 小结


```java
 void setHeader(String name, String value)  用给定名称和值设置响应头
案例需求

设置响应头location实现进行重定向跳转页面

实现代码

运行测试

访问地址

http://localhost:8080/day26_servletcontext_response_war_exploded/_01LocationServlet

运行效果

[外链图片转存失败(img-kRDxSVpi-1566098107679)(assets/)]

跳转后地址

http://localhost:8080/day26_servletcontext_response_war_exploded/_01GetFileServlet_Path
小结
  • location的含义?

    响应头重定向跳转页面地址的设置
    
  • 推荐重定向跳转的api方法?

    response.sendRedirect(url);
    

08.响应头2-ContentType解决输出中文乱码【应用】

目标

掌握使用响应头Content-Type解决响应体字符流输出中文乱码问题

理解乱码原理

响应头ContentType格式

[外链图片转存失败(img-Vj7ttkuw-1566098107680)(assets/]

服务器发送的数据类型,又叫MIME类型

产生乱码的演示

演示要求

使用response字符流输出“传智”给浏览器显示,观察乱码

代码结构

[外链图片转存失败(img-RXW3wD0o-1566098107680)(assets/)] 

代码

package com.itheima.response._响应头;

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(name = "_02ContentTypeServlet_LuanMa", urlPatterns = "/_02ContentTypeServlet_LuanMa")
public class _02ContentTypeServlet_LuanMa extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

       //演示响应体输出中文字符流乱码
        response.getWriter().write("传智"); //浏览器显示“??”

    }
}

运行访问地址

http://localhost:8080/day26_servletcontext_response_war_exploded/_02ContentTypeServlet_LuanMa

运行乱码效果

[外链图片转存失败(img-PzZBjQwV-1566098107681)(assets/)]

解决输出乱码实现分析

[外链图片转存失败(img-Q5dzGwp5-1566098107682)(assets/)]

对传智进行普通编码得到字节数组

[外链图片转存失败(img-7KyTzK6L-1566098107682)(assets/)]

解决输出中文乱码的代码

代码结构

[外链图片转存失败(img-EIBA0iFm-1566098107682)(assets/)] 

实现代码

package com.itheima.response._响应头;

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(name = "_03ContentTypeServlet_NOLuanMa", urlPatterns = "/_03ContentTypeServlet_NOLuanMa")
public class _03ContentTypeServlet_NOLuanMa extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //目标:使用原理底层解决输出中文乱码

        //1.设置服务器输出字符流码表为utf8
        response.setCharacterEncoding("utf-8");
        //2.设置浏览的码表为utf8
        response.setHeader("content-type","text/html;charset=utf-8");

        //注意:上面是原理代码,但是推荐使用response.setContentType("text/html;charset=utf-8")解决,原理就是上面2句

       //演示响应体输出中文字符流乱码
        response.getWriter().write("传智"); //浏览器显示“??”

    }
}

运行访问地址

http://localhost:8080/day26_servletcontext_response_war_exploded/_03ContentTypeServlet_NOLuanMa

运行效果

[外链图片转存失败(img-i8iuKLSZ-1566098107683)(assets/)]

小结
  • 响应头content-Type含义?

    设置服务器响应数据的类型与浏览器使用码表
    
  • 解决response输出字符流数据乱码推荐代码为?

    response.setContentType("text/html;charset=utf8");
    

09.响应头3-refresh实现定时跳转页面【应用】

目标

利用refresh响应头可以解决倒计时跳转到指定的页面

格式
Refresh: 1;url=hello.html

含义:1秒以后跳转hello.html页面

案例需求

访问servlet显示一个倒计时页面,倒计时结束后跳转到指定的资源

实现代码
package com.itheima.response._响应头;

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(name = "_04RefreshServlet", urlPatterns = "/_04RefreshServlet")
public class _04RefreshServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //目标:倒计时3秒后跳转到资源“/_01GetFileServlet_Path”

        //设置响应头refresh实现
        response.setHeader("refresh","3;url="+request.getContextPath()+"/_01GetFileServlet_Path");

        //转发跳转到refresh页面显示倒计时3秒,3秒到时候就会执行上面的命令
        request.getRequestDispatcher("/refresh.html").forward(request,response);//服务器内部跳转第一个“/”代表当前项目内

    }
}
运行效果

访问地址

http://localhost:8080/day26_servletcontext_response_war_exploded/_04RefreshServlet

最后跳转地址

http://localhost:8080/day26_servletcontext_response_war_exploded/_01GetFileServlet_Path

运行效果

[外链图片转存失败(img-TQyxou3P-1566098107683)(assets/)]

小结
  • refresh响应头的格式:refresh:2;url=index.html; 请求说出含义?

    2秒后跳转到index.html页面
    

10.响应头4-ContentDisposition实现附件下载【应用】

目标

使用ContentDisposition响应头实现浏览器附件下载资源文件

格式

格式: Content-Disposition: attachment; filename=aa.zip

[外链图片转存失败(img-I0s5dM07-1566098107684)(assets/)]

案例需求

浏览器请求servlet输出一张图片给浏览器,浏览器下载附件方式接收数据,文件名为非中文

实现代码

代码位置

[外链图片转存失败(img-q08WYOUk-1566098107684)(assets/)] 

代码

package com.itheima.response._响应头;

import org.apache.commons.io.IOUtils;

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

@WebServlet(name = "_05ContentDispositionServlet_NoChinese", urlPatterns = "/_05ContentDispositionServlet_NoChinese")
public class _05ContentDispositionServlet_NoChinese extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //目标:读取一个资源文件图片输出给浏览器,浏览器采用附件下载

        //设置响应头content-disposition让浏览器附件下载,浏览器下载的文件名字为5.jpg
        response.setHeader("content-disposition","attachment;filename=5.jpg");

        //1.获取资源文件在部署位置的【文件的输入流】
        ServletContext application = getServletContext();
        InputStream inputStream = application.getResourceAsStream("/img/5.jpg");


        //3.将输入流的数据写出到输出流,显示到浏览器

        OutputStream outputStream = response.getOutputStream();

        //使用工具类将输入流数据写入到输出流
        IOUtils.copy(inputStream,outputStream);

        //关闭流
        inputStream.close();

    }
}
运行效果

访问地址

http://localhost:8080/day26_servletcontext_response_war_exploded/_05ContentDispositionServlet_NoChinese

效果

[外链图片转存失败(img-idnQzPKE-1566098107684)(assets/)]

案例需求升级【了解】

浏览器请求servlet输出一张图片给浏览器,浏览器下载附件方式接收数据,文件名为中文

中文文件名附件下载问题实现分析

下载文件名是通过响应头传递给浏览器的,然而响应头是不可以传输中文的,否则会变成下划线

中文文件名附件下载问题演示

代码位置

[外链图片转存失败(img-d326caHj-1566098107685)(assets/)] 

实现代码

package com.itheima.response._响应头;

import org.apache.commons.io.IOUtils;

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

@WebServlet(name = "_06ContentDispositionServlet_Chinese", urlPatterns = "/_06ContentDispositionServlet_Chinese")
public class _06ContentDispositionServlet_Chinese extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //目标:读取一个资源文件图片输出给浏览器,浏览器采用附件下载【文件名为中文】

        //设置响应头content-disposition让浏览器附件下载,浏览器下载的文件名字为美女.jpg
        response.setHeader("content-disposition","attachment;filename=美女.jpg");

        //1.获取资源文件在部署位置的【文件的输入流】
        ServletContext application = getServletContext();
        InputStream inputStream = application.getResourceAsStream("/img/5.jpg");


        //3.将输入流的数据写出到输出流,显示到浏览器

        OutputStream outputStream = response.getOutputStream();

        //使用工具类将输入流数据写入到输出流
        IOUtils.copy(inputStream,outputStream);

        //关闭流
        inputStream.close();

    }
}

运行访问地址

http://localhost:8080/day26_servletcontext_response_war_exploded/_06ContentDispositionServlet_Chinese

运行效果

[外链图片转存失败(img-ScQyfQF6-1566098107685)(assets/)]

解决中文文件名下载实现分析

响应头不能直接传递中文,所以采用对中文文件名进行url编码传输给浏览器,浏览器会自动url解码显示

不同的浏览器url编码规则不一致,需要判断不同的浏览器版本采取对应url编码规则

规则是:IE浏览器采用java的url编码, 其他浏览器采用base64的url编码

[外链图片转存失败(img-XKg7nIKD-1566098107685)(assets/)]

中文文件名url编码素材代码【了解】
String fileName="美女.jpg";
String ua = request.getHeader("User-Agent");
boolean IE_LT11 = ua.contains("MSIE"); // IE11以下版本
boolean IE11 = ua.contains("rv:11.0) like Gecko"); // IE11
boolean Edge = ua.contains("Edge"); // win10自带的Edge浏览器
// 如果是微软的浏览器,直接进行UTF-8编码
if (IE_LT11 || IE11 || Edge) {
    fileName = URLEncoder.encode(fileName, "UTF-8");
    //由于编码数据给到浏览器,浏览器会自动url解码,所以jav编码的数据必须浏览器可以识别
    // java的编码方式和浏览器有略微的不同:对于空格,java编码后的结果是加号,
    // 而浏览器的编码结果是%20,因此将+替换成%20, 这样浏览器才能正确解析空格
    fileName = fileName.replace("+", "%20");
}
// 标准浏览器使用Base64编码
else {
    Base64.Encoder encoder = Base64.getEncoder();
    fileName = encoder.encodeToString(fileName.getBytes("utf-8"));
    // =?utf-8?B?文件名?= 是告诉浏览器以Base64进行解码
    fileName = "=?utf-8?B?" + fileName + "?=";
}
解决中文文件下载实现代码

代码位置

[外链图片转存失败(img-HVPCXgDP-1566098107685)(assets/)] 

实现代码

package com.itheima.response._响应头;

import org.apache.commons.io.IOUtils;

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

@WebServlet(name = "_07ContentDispositionServlet_ChineseOK", urlPatterns = "/_07ContentDispositionServlet_ChineseOK")
public class _07ContentDispositionServlet_ChineseOK extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //目标:读取一个资源文件图片输出给浏览器,浏览器采用附件下载【文件名为中文】

        //判断不同浏览器版本对中文文件名进行url编码操作
        String fileName="美女.jpg";
        String ua = request.getHeader("User-Agent");
        boolean IE_LT11 = ua.contains("MSIE"); // IE11以下版本
        boolean IE11 = ua.contains("rv:11.0) like Gecko"); // IE11
        boolean Edge = ua.contains("Edge"); // win10自带的Edge浏览器
        // 如果是微软的浏览器,直接进行UTF-8编码
        if (IE_LT11 || IE11 || Edge) {
            fileName = URLEncoder.encode(fileName, "UTF-8");
            //由于编码数据给到浏览器,浏览器会自动url解码,所以jav编码的数据必须浏览器可以识别
            // java的编码方式和浏览器有略微的不同:对于空格,java编码后的结果是加号,
            // 而浏览器的编码结果是%20,因此将+替换成%20, 这样浏览器才能正确解析空格
            fileName = fileName.replace("+", "%20");
        }
        // 标准浏览器使用Base64编码
        else {
            Base64.Encoder encoder = Base64.getEncoder();
            fileName = encoder.encodeToString(fileName.getBytes("utf-8"));
            // =?utf-8?B?文件名?= 是告诉浏览器以Base64进行解码
            fileName = "=?utf-8?B?" + fileName + "?=";
        }

        //设置响应头content-disposition让浏览器附件下载,浏览器下载的文件名字为美女.jpg
        response.setHeader("content-disposition","attachment;filename="+fileName);

        //1.获取资源文件在部署位置的【文件的输入流】
        ServletContext application = getServletContext();
        InputStream inputStream = application.getResourceAsStream("/img/5.jpg");


        //3.将输入流的数据写出到输出流,显示到浏览器

        OutputStream outputStream = response.getOutputStream();

        //使用工具类将输入流数据写入到输出流
        IOUtils.copy(inputStream,outputStream);

        //关闭流
        inputStream.close();

    }
}

访问地址

http://localhost:8080/day26_servletcontext_response_war_exploded/_07ContentDispositionServlet_ChineseOK

运行效果

[外链图片转存失败(img-QaMoxEvw-1566098107686)(assets/)]

小结
  • 响应头Content-Disposition含义?

    设置浏览器采用附件下载资源数据和设置下载文件的名字
    

11.响应头4-附件下载不同类型的文件案例【了解】

目标

下载不同类型的资源文件

需求

根据提供的下载文件素材与页面,实现下载功能

导入如下资源

在web目录下创建download目录,并导入素材下载资源如图

[外链图片转存失败(img-LhkCmnSR-1566098107687)(assets/)] 

实现代码

html页面代码(素材代码,直接使用)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>

        function isIE(){
            //获取当前浏览器相关信息
            var explorer = window.navigator.userAgent.toLowerCase() ;
            //判断是否是ie浏览器
            if (explorer.indexOf("msie") >= 0 || explorer.indexOf("rv:11.0) like gecko") >= 0) {
                return true;
            }else {
                false;
            }
        }
        window.onload = function () {
            if(isIE()){
                //在是IE浏览器的情况下,对中文请求参数编码
                var str = document.getElementById("ww").href;
                var str = encodeURI(str);
                document.getElementById("ww").href = str;
            }
        };
    </script>
</head>
<body>
<a href="download?fileName=ff.jpg">ff.jpg</a>
<a href="download?fileName=file.jpg">file.jpg</a>
<a href="download?fileName=file.txt">file.txt</a>
<!--IE浏览器get方式传递中文不会进行url编码,需要手动使用浏览器的url编码 -->
<a id="ww" href="download?fileName=美女.jpg">美女.jpg</a>
<a href="download?fileName=file.zip">file.zip</a>

</body>
</html>

Servlet代码

package com.itheima.response._响应头;

import org.apache.commons.io.IOUtils;

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

@WebServlet(name = "_08DownLoadFileServlet", urlPatterns = "/download")
public class _08DownLoadFileServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //目标:获取前端需要下载的文件名字,根据文件名字将服务器对应的资源文件响应给浏览下载

        //1.获取要下载的文件名字参数fileName
        String fileName = request.getParameter("fileName");

        //2.获取文件名对应资源文件的输入流
        InputStream inputStream = getServletContext().getResourceAsStream("/download/" + fileName);

        //2.1文件名字有可能为中文,所以要对象进行url编码
        String ua = request.getHeader("User-Agent");
        boolean IE_LT11 = ua.contains("MSIE"); // IE11以下版本
        boolean IE11 = ua.contains("rv:11.0) like Gecko"); // IE11
        boolean Edge = ua.contains("Edge"); // win10自带的Edge浏览器
        // 如果是微软的浏览器,直接进行UTF-8编码
        if (IE_LT11 || IE11 || Edge) {
            fileName = URLEncoder.encode(fileName, "UTF-8");
            //由于编码数据给到浏览器,浏览器会自动url解码,所以jav编码的数据必须浏览器可以识别
            // java的编码方式和浏览器有略微的不同:对于空格,java编码后的结果是加号,
            // 而浏览器的编码结果是%20,因此将+替换成%20, 这样浏览器才能正确解析空格
            fileName = fileName.replace("+", "%20");
        }
        // 标准浏览器使用Base64编码
        else {
            Base64.Encoder encoder = Base64.getEncoder();
            fileName = encoder.encodeToString(fileName.getBytes("utf-8"));
            // =?utf-8?B?文件名?= 是告诉浏览器以Base64进行解码
            fileName = "=?utf-8?B?" + fileName + "?=";
        }



        //2.2 设置响应头采用附件下载
        response.setHeader("content-disposition","attachment;filename="+fileName);



        //3.获取服务器输出流
        OutputStream outputStream = response.getOutputStream();

        //4.使用工具类将输入流数据写入到输出流
        IOUtils.copy(inputStream,outputStream);

        //5.输入流要关闭
        inputStream.close();

    }
}
运行效果

访问地址

http://localhost:8080/day26_servletcontext_response_war_exploded/down.html

运行效果

[外链图片转存失败(img-ue6Rw5w3-1566098107687)(assets/]

小结
  • 响应头如何传递中文给浏览器显示?

    对中文手动url编码操作
    
  • 不同浏览器对url编码格式一致吗?

    IE浏览器是采用的java的URL编码
    其他浏览器采用base64的URL编码
    
  
  

### 12.响应头5-content-encoding响应头数据压缩【应用】

##### 目标

服务器使用响应头content-encoding通知浏览器解压文件后显示数据

##### 需求

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

  1. 有一串大的字符串数据,在服务器上输出压缩之前数据的大小
  2. 使用 java.util.zip.GZIPOutputStream 压缩以后,在浏览器响应头这边观察压缩以后数据的大小。

##### GZIPOutputStream 类的方法

构造方法:
GZIPOutputStream(OutputStream out) 使用默认缓冲区大小创建新的输出流。
方法:
public void write(byte[] b) 将字节数组写入压缩输出流。
void finish() 完成将压缩数据写入输出流的操作,无需关闭底层流。


##### 实现步骤

  1. 如果需要对数据进行压缩,使用压缩流,使用输出流做为参数。
    GZIPOutputStream gzipOutputStream = newGZIPOutputStream(response.getOutputStream());
  2. gzipOutputStream 的 write 方法首先会对数据进行压缩处理,然后调用传递进去 OutputStream 对象的write 方法把压缩的数据写出去。
    gzipOutputStream.write(sb.toString().getBytes());
  3. 完成将压缩数据写入输出流的操作,如果没有结束,则浏览器上看不到东西。
    gzipOutputStream.finish();

##### 要点

一开始要设置相应响应头信息,告诉浏览器目前内容是以压缩包的形式传输的,不然会以附件的方式下载下
来了。 response.setHeader(“Content-Encoding”,“gzip”);


##### gzip压缩介绍

Gzip是如何压缩的
简单来说, Gzip压缩是在一个文本文件中找出类似的字符串, 并临时替换他们,使整个文件变小。这种形式的压缩对Web来说非常适合, 因为HTML和CSS文件通常包含大量的重复的字符串,例如空格,标签。

Gzip压缩的好处
http压缩对纯文本可以压缩至原内容的40%, 从而节省了60%的数据传输。

Gzip的缺点
JPEG这类文件用gzip压缩的不够好。




##### 实现代码

```java
package com.itheima.response._响应头;

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.zip.GZIPOutputStream;

@WebServlet(name = "_09ContentEncodingServlet", urlPatterns = "/_09ContentEncodingServlet")
public class _09ContentEncodingServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //目标:输出一个大量的文本字符串,对其使用gzip压缩,输出给浏览器,浏览器解压显示

        //1.拼接一个大量的字符串
        StringBuilder stringBuilder = new StringBuilder("abcd");
        for (int i = 0; i < 1000 ; i++) {
            stringBuilder.append("abcd");
        }

        //输出压缩前的字节数
        System.out.println("压缩前字节数:"+stringBuilder.toString().getBytes().length);

        //2.设置响应头通知浏览器解压显示数据
        response.setHeader("content-encoding","gzip");

        //3.使用gzip压缩大量的文本字符串输出给浏览器

        //创建压缩输出流对象
        GZIPOutputStream gzipOutputStream = new GZIPOutputStream(response.getOutputStream());
        //压缩数据到内存中
        gzipOutputStream.write(stringBuilder.toString().getBytes());
        //将内存中的压缩数据给到输出流到浏览器
        gzipOutputStream.finish();


    }
}
运行效果

访问地址

http://localhost:8080/day26_servletcontext_response_war_exploded/_09ContentEncodingServlet

压缩前控制台的结果

[外链图片转存失败(img-oKeqlkCi-1566098107688)(assets/)]

压缩后浏览器展现的结果

[外链图片转存失败(img-ur2OB3aO-1566098107688)(assets/)]

小结
  • 响应头content-encoding的含义?

    通知浏览器解压显示数据
    
  • 文本数据压缩工具类是什么?

    GZIPOutputStream
    

13.响应体-验证码案例实现分析【理解】

目标

使用响应体输出动态的验证码图片

响应体的介绍

就是输出网页的数据,给用户看的

1.输出字节流数据,动态资源图片的输出、视频、音频

2.输出字符流

案例-响应体输出验证码-实现原理
验证码就是一张图片,验证码图片不是真实的资源图片而是缓存图片,图片的数据在缓存里面或内存里面,将内存中的缓存图片输出到浏览器上
java绘图相关类

[外链图片转存失败(img-KJVhdiec-1566098107689)(assets/)]

java.awt.Graphics 类中的画图方法

[外链图片转存失败(img-pBkGkyJq-1566098107689)(assets/)]

14.响应体-验证码案例1-servlet代码实现【应用】

目标

使用servlet进绘制内存验证码图片输出到浏览器

实现代码
package com.itheima.response._响应体;

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(name = "CheckCodeServlet", urlPatterns = "/CheckCodeServlet")
public class CheckCodeServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    //定义随机类,方便其他方法调用
    private Random random = new Random();

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //目标:使用java代码绘制内存图片输出到浏览器显示

        //1.创建一个内存图片
        //构造方法:new BufferedImage(宽度像素,高度像素,图片模式)
        BufferedImage image = new BufferedImage(150,50,BufferedImage.TYPE_INT_RGB);

        //2.获取一个画笔(绘制对象,用于画图)
        Graphics g = image.getGraphics();

        //3.填充矩形区域
        //绘制矩形区域的方法:g.fillRect(x,y,宽度,高度);
        g.fillRect(0,0,150,50);

        //设置画笔的颜色
        g.setColor(Color.blue);

        //给矩形区域画边框
        //语法:g.drawRect(x,y,宽度,高度);
        g.drawRect(1,1,147,47); //注意边框不要与矩形完成重合


        //4.在图片上画4条干扰直线
        //语法:g.drawLine(x1,y1,x2,y2); 根据2个点的坐标进行画直线
        for (int i = 0; i < 4; i++) {

            //设置随机颜色
            g.setColor(getRandomColor());

            //使用随机类获取2个坐标的位置
            int x1 = random.nextInt(147);
            int y1 = random.nextInt(47);

            int x2 = random.nextInt(147);
            int y2 = random.nextInt(47);

            g.drawLine(x1,y1,x2,y2);
        }

        //5.在图片上画验证码
        //定义随机生成验证码字符串的范围
        String checkCodeString = "qwertyupasdfghjkzxcvbnmQWERTYUPASDFGHJKLZXCVBNM2346789传智播客";
        //定义验证码字符串拼接随机生成的每个字符,用于以后验证
        StringBuilder checkCode = new StringBuilder();
        //从字符串的范围里面随机获取4个字符
        for (int i = 0; i <4 ; i++) {

            //获取一个随机的位置
            int index  = random.nextInt(checkCodeString.length());

            //根据位置获取指定的字符
            char c = checkCodeString.charAt(index);

            //设置画笔画字符串的字体
            //创建字体对象的语法:new Font(字体类型,加粗或斜体,字号);
            g.setFont(new Font("微软雅黑",Font.BOLD,20));

            //将每个字符画到图片上
            //g.drawString(字符串,x,y); 将指定的字符串画到指定的坐标位置
            g.drawString(c+"",30+30*i,30);

            //拼接每个字符
            checkCode.append(c);
        }

        //6.输出内存图片到浏览器显示
        //语法:ImageIO.write(内存图片对象,图片的扩展名格式,输出到浏览器的字节输出流)
        ImageIO.write(image,"png",response.getOutputStream());
    }

    //用于创建返回随机颜色对象
    private Color getRandomColor(){
        int r = random.nextInt(256);//0~255
        int g = random.nextInt(256);//0~255
        int b = random.nextInt(256);//0~255

        //构造方法:new Color(r,g,b); //没有颜色值范围0~255
        return new Color(r,g,b);
    }
}
效果

访问地址

http://localhost:8080/day26_servletcontext_response_war_exploded/CheckCodeServlet

运行效果

[外链图片转存失败(img-sgwXtdXZ-1566098107689)(assets/)]

小结

BufferedImag对象,内存图片对象,可以根据组对象进行绘制图片

ImagIO.write(),可以将内存图片输出给response,respone将内存输出到浏览器

15.响应体-验证码案例2-页面实现验证码切换【应用】

目标

将验证码图片放到登录页面使用

并且在登录页面实现点击验证码切换验证码

实现步骤
  1. 打开之前开发好的登录功能项目

  2. 将输出验证码图片的Servlet放入到登录项目中

  3. 到login.html页面增加验证码输入框与图片显示

  4. 实现点击验证码图片切换验证码

  5. 运行部署登录项目

实现代码

[外链图片转存失败(img-AQdTzGm4-1566098107690)(assets/)] 

登录页面login.html实现切换验证码

[外链图片转存失败(img-vJlYN7up-1566098107690)(assets)]

部署项目运行

[外链图片转存失败(img-CwGtWPNa-1566098107690)(assets/)]

效果

[外链图片转存失败(img-DtKCWXzg-1566098107690)(assets/)]

16.总结

  1. ServletContext
    • 读取项目内资源文件,尤其web目录下使用(如果是src目录下建议使用类路径方式)
    • 是全局上下文域对象,存储的数据所有用户所有请求共享
    • 读取全局配置参数
  2. response
    • 响应数据对象 HttpServletResponse
    • 响应数据组成
      • 响应行
      • 响应头
      • 响应体
    • 响应行,http通信状态码
      • 200,通信正常,正确从服务器获取了资源
      • 302,重定向跳转
      • 304,数据从客户端浏览器缓存中获取的
      • 404,找不到资源,一般是url写错了,或者是资源没有成功部署到服务器上
      • 405,请求servlet,servlet没有对应的doGet或doPost处理请求方法
      • 500,服务器发生异常了,内在错误占99%(代码错误),外在错误占1%(网络连接不通)
    • 响应头
      • location, 设置重定向跳转的地址
      • content-type, 设置响应数据类型和浏览器码表
      • refresh, 定时跳转
      • content-dispositioin, 设置附件下载
      • content-encoding, 设置浏览器解压显示数据
    • 响应体
      • 字节流输出
      • 字符流输出, 注意乱码

,30);

        //拼接每个字符
        checkCode.append(c);
    }

    //6.输出内存图片到浏览器显示
    //语法:ImageIO.write(内存图片对象,图片的扩展名格式,输出到浏览器的字节输出流)
    ImageIO.write(image,"png",response.getOutputStream());
}

//用于创建返回随机颜色对象
private Color getRandomColor(){
    int r = random.nextInt(256);//0~255
    int g = random.nextInt(256);//0~255
    int b = random.nextInt(256);//0~255

    //构造方法:new Color(r,g,b); //没有颜色值范围0~255
    return new Color(r,g,b);
}

}


##### 效果

访问地址

http://localhost:8080/day26_servletcontext_response_war_exploded/CheckCodeServlet



##### 小结

BufferedImag对象,内存图片对象,可以根据组对象进行绘制图片

ImagIO.write(),可以将内存图片输出给response,respone将内存输出到浏览器


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值