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/图片资源文件,并输出到浏览器显示
实现步骤
- 在项目中准备一个资源文件
- 创建一个Servlet通过ServletContext对象读取资源文件
- 动态方式向浏览器输出资源文件
实现代码
代码结构
代码
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
运行效果
servletContext获取文件的输入流方式读取文件
代码结构
实现代码
访问地址
http://localhost:8080/day26_servletcontext_response_war_exploded/_02GetFileServlet_Stream
运行效果
疑问:以前学习根据类路径读取文件和现在ServletContext读取文件,以后使用哪个?
jdbc.properties在src目录下
读取src类路径下面的文件推荐使用类路径方式
读取web项目其他位置推荐使用ServletConext方式
使用类路径读取文件输入流
方式一: Demo.class.getResourceAsStream("/jdbc.propertes") //第一个/代表src目录
方式二: getServletContext().getResourceAsStream("/WEB-INF/classes/jdbc.propertes"); //路径中的第一个/代表当前项目内
使用第三方工具类简化流读写操作
第三方工具包
工具类api方法
实现步骤
1.导入第三方工具包commons-io-2.1.jar到项目中WEB-INF/lib目录下并引用
2.使用工具类方法实现将输入流数据自动写入到输出流显示
导入jar包
代码位置
实现代码
运行效果与上面一样
小结
-
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对象读取全局参数并打印输出
实现代码
代码结构
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
效果
04.ServletContext作用3—上下文域对象【应用】
目标
使用上下文域对象存储所有动态资源共享的数据
域对象的作用(内存对象)
用于实现动态资源之间传递数据
上下文域对象介绍
从服务器开启到服务器关闭都是有效的,所有的用户所有的 Servlet 之间都可以实现数据共享。
作用域对象有三个:request请求域对象,会话域对象,上下文域对象(servletcontext)
凡是作用域对象都有3个方法如下
应用场景
适合统计所有用户共享的数据,比如:统计登录的人数
案例需求
每个用户进行登录,登录后显示“第几个用户登录”
实现过程
实现步骤
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>
实现代码
代码结构
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
效果
小结
-
请求说出目前学习过的域对象有哪些?
request,请求域 servletContext,上下文域
-
这些域对象的区别?
request,请求域, 一个客户端一次请求内 servletContext,上下文域, 所有客户端所有请求都有效,全局共享
-
响应数据有几部分组成?
响应行,头,体
06.响应行-组成与状态码介绍【理解】
目标
理解响应行的数据组成
掌握常见6种通信状态码
响应行的数据组成
HTTP/1.1 200 OK
http协议版本
200,http协议状态码,描述服务器通信的状态
ok,状态码的翻译,代表通信正常, tomcat8服务器没有这个翻译, 之前的版本有
常见http状态码
404发生的情况
访问的url写错了(几率最大)
服务器没有这个资源
编写响应行代码,设置错误代码输出404错误
response设置响应行状态码的方法
response对象介绍
所属类型HttpServletResponse接口
它是一个接口,没有具体的实现。由tomcat去实现,并且在运行的时候实例化,传递给Servlet的处理请求方法参数response
设置状态码方法
代码结构
实现代码
```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
运行效果
跳转后地址
http://localhost:8080/day26_servletcontext_response_war_exploded/_01GetFileServlet_Path
小结
-
location的含义?
响应头重定向跳转页面地址的设置
-
推荐重定向跳转的api方法?
response.sendRedirect(url);
08.响应头2-ContentType解决输出中文乱码【应用】
目标
掌握使用响应头Content-Type解决响应体字符流输出中文乱码问题
理解乱码原理
响应头ContentType格式
服务器发送的数据类型,又叫MIME类型
产生乱码的演示
演示要求
使用response字符流输出“传智”给浏览器显示,观察乱码
代码结构
代码
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
运行乱码效果
解决输出乱码实现分析
对传智进行普通编码得到字节数组
解决输出中文乱码的代码
代码结构
实现代码
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
运行效果
小结
-
响应头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
运行效果
小结
-
refresh响应头的格式:refresh:2;url=index.html; 请求说出含义?
2秒后跳转到index.html页面
10.响应头4-ContentDisposition实现附件下载【应用】
目标
使用ContentDisposition响应头实现浏览器附件下载资源文件
格式
格式: Content-Disposition: attachment; filename=aa.zip
案例需求
浏览器请求servlet输出一张图片给浏览器,浏览器下载附件方式接收数据,文件名为非中文
实现代码
代码位置
代码
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
效果
案例需求升级【了解】
浏览器请求servlet输出一张图片给浏览器,浏览器下载附件方式接收数据,文件名为中文
中文文件名附件下载问题实现分析
下载文件名是通过响应头传递给浏览器的,然而响应头是不可以传输中文的,否则会变成下划线
中文文件名附件下载问题演示
代码位置
实现代码
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
运行效果
解决中文文件名下载实现分析
响应头不能直接传递中文,所以采用对中文文件名进行url编码传输给浏览器,浏览器会自动url解码显示
不同的浏览器url编码规则不一致,需要判断不同的浏览器版本采取对应url编码规则
规则是:IE浏览器采用java的url编码, 其他浏览器采用base64的url编码
中文文件名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 + "?=";
}
解决中文文件下载实现代码
代码位置
实现代码
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
运行效果
小结
-
响应头Content-Disposition含义?
设置浏览器采用附件下载资源数据和设置下载文件的名字
11.响应头4-附件下载不同类型的文件案例【了解】
目标
下载不同类型的资源文件
需求
根据提供的下载文件素材与页面,实现下载功能
导入如下资源
在web目录下创建download目录,并导入素材下载资源如图
实现代码
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
运行效果
小结
-
响应头如何传递中文给浏览器显示?
对中文手动url编码操作
-
不同浏览器对url编码格式一致吗?
IE浏览器是采用的java的URL编码 其他浏览器采用base64的URL编码
### 12.响应头5-content-encoding响应头数据压缩【应用】
##### 目标
服务器使用响应头content-encoding通知浏览器解压文件后显示数据
##### 需求
使用数据压缩之后再从服务器传输数据到浏览器, 可以减少网络的传输量,提高网页的下载速度。
- 有一串大的字符串数据,在服务器上输出压缩之前数据的大小
- 使用 java.util.zip.GZIPOutputStream 压缩以后,在浏览器响应头这边观察压缩以后数据的大小。
##### GZIPOutputStream 类的方法
构造方法:
GZIPOutputStream(OutputStream out) 使用默认缓冲区大小创建新的输出流。
方法:
public void write(byte[] b) 将字节数组写入压缩输出流。
void finish() 完成将压缩数据写入输出流的操作,无需关闭底层流。
##### 实现步骤
- 如果需要对数据进行压缩,使用压缩流,使用输出流做为参数。
GZIPOutputStream gzipOutputStream = newGZIPOutputStream(response.getOutputStream()); - gzipOutputStream 的 write 方法首先会对数据进行压缩处理,然后调用传递进去 OutputStream 对象的write 方法把压缩的数据写出去。
gzipOutputStream.write(sb.toString().getBytes()); - 完成将压缩数据写入输出流的操作,如果没有结束,则浏览器上看不到东西。
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
压缩前控制台的结果
压缩后浏览器展现的结果
小结
-
响应头content-encoding的含义?
通知浏览器解压显示数据
-
文本数据压缩工具类是什么?
GZIPOutputStream
13.响应体-验证码案例实现分析【理解】
目标
使用响应体输出动态的验证码图片
响应体的介绍
就是输出网页的数据,给用户看的
1.输出字节流数据,动态资源图片的输出、视频、音频
2.输出字符流
案例-响应体输出验证码-实现原理
验证码就是一张图片,验证码图片不是真实的资源图片而是缓存图片,图片的数据在缓存里面或内存里面,将内存中的缓存图片输出到浏览器上
java绘图相关类
java.awt.Graphics 类中的画图方法
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
运行效果
小结
BufferedImag对象,内存图片对象,可以根据组对象进行绘制图片
ImagIO.write(),可以将内存图片输出给response,respone将内存输出到浏览器
15.响应体-验证码案例2-页面实现验证码切换【应用】
目标
将验证码图片放到登录页面使用
并且在登录页面实现点击验证码切换验证码
实现步骤
-
打开之前开发好的登录功能项目
-
将输出验证码图片的Servlet放入到登录项目中
-
到login.html页面增加验证码输入框与图片显示
-
实现点击验证码图片切换验证码
-
运行部署登录项目
实现代码
登录页面login.html实现切换验证码
部署项目运行
效果
16.总结
- ServletContext
- 读取项目内资源文件,尤其web目录下使用(如果是src目录下建议使用类路径方式)
- 是全局上下文域对象,存储的数据所有用户所有请求共享
- 读取全局配置参数
- 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将内存输出到浏览器