目录
- ServletContext:读取文件
- ServletContext:读取全局参数(web.xml)
- ServletContext:上下文域对象
- http协议响应数据的组成
- 1、响应行
- 2、响应头
- 3、响应体
ServletContext:读取文件
ServletContext称为上下文对象,每个 Web应用程序都对应了一个上下文对象。所以ServletContext的作用范围是整个应用程序,全局唯一的对象
//1.根据相对路径"/img/5.jpg"读取运行位置资源文件真实路径(ServletContext)
String path = getServletContext().getRealPath("/img/5.jpg");
System.out.println("img/5.jpg的真实绝对路径:"+path);
//2.根据资源文件绝对真实路径获取字节输入流
InputStream inputStream = new FileInputStream(path);
//3.将输入流的数据输出到浏览器直接输出流中,最终在浏览器上展现图片
byte[] bytes = new byte[1024];
int length = -1;
while ((length = inputStream.read(bytes))!=-1){
//输出(response的输出字节流:response.getOutputStream())
response.getOutputStream().write(bytes,0,length);
}
//关闭输入流
inputStream.close();
//注意:输出流不用关闭,因为tomcat服务器会关闭的
注意:
response.getOutputStream()不需要关闭,因为tomcat会自动关闭
//1.根据相对路径"/img/5.jpg"读取资源文件的输入流
InputStream inputStream = getServletContext().getResourceAsStream("/img/5.jpg");
//2.将输入流的数据输出到浏览器直接输出流中,最终在浏览器上展现图片
byte[] bytes = new byte[1024];
int length = -1;
while ((length = inputStream.read(bytes))!=-1){
//输出(response的输出字节流:response.getOutputStream())
response.getOutputStream().write(bytes,0,length);
}
//关闭输入流
inputStream.close();
//注意:输出流不用关闭,因为tomcat服务器会关闭的
使用IOUtils工具类优化流读写操作
注意:
lib放在WEB-INF下面
//1.根据相对路径"/img/5.jpg"读取资源文件的输入流
InputStream inputStream = getServletContext().getResourceAsStream("/img/5.jpg");
//2.将输入流的数据输出到浏览器直接输出流中,最终在浏览器上展现图片
IOUtils.copy(inputStream,response.getOutputStream());
ServletContext:读取全局参数(web.xml)
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">
<!--
全局参数
-->
<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>
//1.获取全局参数名称列表
Enumeration<String> enumeration = getServletContext().getInitParameterNames();
//2.遍历迭代名称列表, 获取每个参数名称, 根据参数名称获取对应的参数值
while (enumeration.hasMoreElements()){
String paramName = enumeration.nextElement();
String paramValue = getServletContext().getInitParameter(paramName);
System.out.println(paramName+"="+paramValue);
}
ServletContext:上下文域对象
上下文域对象的生命周期:从服务器开启到服务器关闭都是有效的,所有的用户所有的 Servlet 之间都可以实现数据共享。
作用域对象有三个:request请求域对象,会话域对象,上下文域对象(servletcontext)
request请求域内存最小
案例:显示第几个用户登录
LoginServlet
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
//定义原子类AtomicInteger存储登录人数
private AtomicInteger count = null;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);//这句代码不可以删除,必须保留,否则直接使用config会报错
//初始化登录人数为:0
count = new AtomicInteger(0);
//将数据写入到上下文域中
getServletContext().setAttribute("count",count);
}
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//目标:处理登录请求,统计登录人数
//处理乱码
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
//1.获取用户名与密码
String username = request.getParameter("username");
String password = request.getParameter("password");
//2.校验登录数据,用户名必须为admin,密码必须为123
if("admin".equals(username) && "123".equals(password)) {
//3.校验通过
// 3.1 登录人数+1,
count.incrementAndGet();
//注意:不用更新回上下文域中,因为具有共同的内存地址,修改这里的数据上下文域对象数据也会修改
// 3.2 跳转到CountServlet去显示登录的人数
response.sendRedirect(request.getContextPath()+"/CountServlet");
}else {
//4.输出用户名或密码错误,登录失败
response.getWriter().print("<script>alert('用户名或密码错误');history.back();</script>");
}
}
}
CountServlet
@WebServlet("/CountServlet")
public class CountServlet extends HttpServlet {
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//目标:从上下文域对象获取统计的登录人数输出
//1.获取登录人数
AtomicInteger count = (AtomicInteger) getServletContext().getAttribute("count");
//2.输出
response.setContentType("text/html;charset=utf8");
response.getWriter().print("您是第"+count.get()+"位登录成功的用户");
}
}
注意:
1、并发量多的时候,使用加锁
2、并发量少的时候,使用原子类, 原子类在高并发情况下可能会出错3、转发跳转共享同一个请求,不需要重新设置编码
4、重定向跳转不共享一个请求,需要重新设置编码,后一次请求会覆盖前一次请求
http协议响应数据的组成
http协议响应数据的组成:
响应行、响应头、空行、响应体
1、响应行
常见http状态码
http协议状态码101~599个,都是系统的状态码,每个状态码都有不同的含义
设置状态码的方法
/*
* 设置响应状态码的,response对象的方法api
* 1、setStatus(int status)
* 仅设置状态码,没有任何效果,常与响应头命令配合使用,不单独使用;
*
* 2、sendError(int sc)
* int sc, 设置的状态码
* 设置状态码并且发送错误状态码页面给浏览器
*
* 3、sendError(int sc,String msg)
* 设置状态码并且发送错误状态码页面给浏览器,并且可以自定义消息内容
* 应用场景:自己开发平台定义逻辑可以给开发人员报错误消息页面
* */
response.sendError(408,"你吃了吗?");
2、响应头
响应头1、location重定向跳转
response.sendRedirect(request.getContextPath()+"/Demo3Servlet")的底层原理。
/*
* 响应头:location
* 格式:location:重定向跳转的地址
* 含义:通知浏览器,让浏览器重定向跳转到指定的地址
* 让浏览重定向跳转必须的条件有2个:
* 设置响应头location
* 设置状态码为302
* 设置响应头方法api:
* response.setHeader(Sting key,String value);
* */
//目标:重定向跳转到Demo3Servlet
//1.设置重定向跳转状态码
response.setStatus(302);
//2.设置location响应头的重定向跳转地址
response.setHeader("location",request.getContextPath()+"/Demo3Servlet");
//以后重定向跳转使用如下代码,因为原理就是上面2句代码
//response.sendRedirect(request.getContextPath()+"/Demo3Servlet");
响应头2、ContentType解决输出乱码
响应头ContentType含义
1、设置服务器响应给浏览器数据的数据类型(MIME类型)(多用途响应类型)
2、设置浏览器接收服务器数据使用的码表
response.setContentType(“text/html;charset=utf-8”)的底层原理:
/*
* 目标:响应头Content-Type
* 格式:content-type:text/html;charset=utf-8;
* 含义:设置2部分内容
* 第一部分:text/html,设置服务响应数据的类型,
* 也叫mime类型,常用mime类:
* text/html ,响应数据为符合html代码规范的文本字符串数据
* text/plain ,响应数据为普通文本字符串数据
* application/json, 响应数据为符合json格式标准的文本字符串数据(以后会讲解)
* 第二部分:charset=utf-8, 设置浏览器使用的字符码表
*
* 作用:设置输出数据格式类型与浏览器使用的字符码表
* 应用场景:用于解决输出中文乱码底层原理就是使用这个响应头
* */
//解决输出中文乱码【推荐方式】
//response.setContentType("text/html;charset=utf-8"); 底层原理就是上面的响应头content-type和服务器响应码表设置
//乱码产生的原理:经历2个过程
//第一个过程:服务器response默认输出中文字符采用的码表是iso8859-1
//第二个过程:浏览器默认码表,中国大陆浏览器默认码表为GBK
//解决乱码需要操作2个过程【原理方式,只需要了解】
//设置服务器response输出码表为utf-8
response.setCharacterEncoding("utf-8");
//设置浏览器码表为utf-8
response.setHeader("content-type","text/html;charset=utf-8");
response.getWriter().print("传智");
响应头3、refresh定时跳转
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
//定义倒计时数字
var count = 3;
//当页面加载完成时调用
window.onload=function(){
//倒计时
tick();
}
//启动倒计时
function tick(){
//判断倒计时数字大于0继续进行倒计时操作
if(count>0){
//获得页面中显示倒计时的标签对象
var p = document.getElementById("one");
//页面显示倒计时数字,显示后数字减1
p.innerHTML=count--;
// if(count==0){
// location.href = "Demo3Servlet";
// }
//设置定时在一秒后继续调用当前方法
window.setTimeout(tick, 1000);
}
}
</script>
</head>
<body>
页面在<p id="one">3</p>秒后跳转...
</body>
</html>
@WebServlet("/RefreshServlet")
public class RefreshServlet extends HttpServlet {
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* 响应头: refresh
* 格式:refresh:秒数;url=/项目路径/资源路径
* 含义:通过响应头倒计时秒数后重定向跳转到指定资源地址【跳转本质:使用重定向】
*
* 注意:
* 倒计时跳转页面,不会使用后端实现,以后推荐完全由前端实现就可以了
* */
//目标:倒计时3秒跳转到指定的资源Demo3Servlet
response.setHeader("refresh","3;url="+request.getContextPath()+"/Demo3Servlet");
//跳转到refresh.html展现倒计时数据
//response.sendRedirect(request.getContextPath()+"/refresh.html");
//这里不可以使用重定向:因为重定向是2次请求,会覆盖掉上面响应头refresh的设置,导致倒计时跳转失效
//使用转发跳转到refresh.html, 这样response对象不会被覆盖
request.getRequestDispatcher("/refresh.html").forward(request,response);
}
}
响应头4、ContentDisposition附件下载
案例:下载不同类型的文件
解决请求体(post)或者请求行(get)非法数据(中文)乱码的问题
在这里插入代码片<!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 {
return 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="Download2Servlet?fileName=ff.jpg">ff.jpg</a>
<a href="Download2Servlet?fileName=file.jpg">file.jpg</a>
<a href="Download2Servlet?fileName=file.txt">file.txt</a>
<!--IE浏览器get方式传递中文不会进行url编码,需要手动使用浏览器的url编码 -->
<a id="ww" href="Download2Servlet?fileName=美女.jpg">美女.jpg</a>
<a href="Download2Servlet?fileName=file.zip">file.zip</a>
</body>
</html>
解决响应头非法数据(中文)乱码的问题
@WebServlet("/Download2Servlet")
public class Download2Servlet extends HttpServlet {
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//目标:处理各种文件下载
//1.获取客户端传递过来要下载的文件名,例如“file.zip”
String fileName = request.getParameter("fileName");
//2.获取指定文件名对应服务器磁盘文件的输入流
InputStream inputStream = getServletContext().getResourceAsStream("/download/" + fileName);
//3.对用户下载后的文件名进行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 + "?=";
}
//4.设置浏览器对数据进行附件下载
response.setHeader("content-disposition","attachment;filename="+fileName);
//5.将文件的输入流输出给response的的字节输出流
IOUtils.copy(inputStream,response.getOutputStream());
}
}
响应头5、content-encoding解压数据
gzip压缩介绍
Gzip压缩是在一个文本文件中找出类似的字符串, 并临时替换他们,使整个文件变小。这种形式的压缩对Web来说非常适合,因为HTML和CSS文件通常包含大量的重复的字符串,例如空格,标签。
/*
* 响应头: content-encoding
* 格式:content-encoding:gzip
* 含义:通知浏览器对http协议传输的gzip压缩文件进行解压显示数据
* gzip是http协议压缩文件规范格式
* 应用场景:当服务器输出大量文本信息会采用压缩方式压缩数据,压缩提高网络传输数据速度,这样浏览器会接收到一个压缩文件
* 浏览器默认接收压缩文件会进行附件下载,可以通过这个响应头设置浏览器会对其解压显示数据
* 文本数据压缩的原理:
* 因为文本字符存在大量的重复数据,压缩的原理去掉重复数据,记录重复的次数,
* 从而减轻传输的数据大小
* 对数据进行压缩语法:
* 创建压缩流对象
* GZIPOutputStream gzipOutputStream = new GZIPOutputStream(response.getOutputStream());
* 对数据进行压缩到缓存中
* gzipOutputStream.write(sb.toString().getBytes());
* 将缓存中的数据输出到输出流
* gzipOutputStream.finish();
* */
//1.压缩前传输大量文本数据:长度4000个字符
StringBuilder stringBuilder =new StringBuilder();
for (int i = 0; i < 1000; i++) {
stringBuilder.append("abcd");
}
//response.getWriter().print(stringBuilder.toString());
//2.压缩后传输大量文本数据:44个字符(压缩将近100倍)
//设置响应头解压gzip文件显示数据
response.setHeader("content-encoding","gzip");
//2.1 创建压缩输出流
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(response.getOutputStream());
//2.2 压缩数据存储在缓存中
gzipOutputStream.write(stringBuilder.toString().getBytes());
//2.3 将压缩数据给到response字节输出流输出到浏览器
gzipOutputStream.finish();
//注意:如果没有设置响应头content-encoding,浏览器默认会接收到一个压缩包并且会附件下载
3、响应体
响应体的介绍
就是输出网页的数据,给用户看的
1.输出字节流数据,动态资源图片的输出、视频、音频
2.输出字符流
案例1-响应体验证码-实现原理
@WebServlet("/CheckCodeServlet")
public class CheckCodeServlet extends HttpServlet {
//定义一个随机类(共享多个地方使用)
private Random random = new Random();
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//目标:输出一个内存图片验证码
//1.创建一个内存图片BufferdImage对象
//构造函数语法:BufferedImage(int width,int height,int imageType)
// width,设置图片的宽度
// height,设置图片的高度
// imageType,设置图片的类型,RGB类型等
BufferedImage image = new BufferedImage(150, 50, BufferedImage.TYPE_INT_RGB);
//2.获取一个画笔(Graphics,具有画图功能)
Graphics g = image.getGraphics();
//3.填充一个矩形区域确定验证码的范围
//设置画笔颜色白色
g.setColor(Color.white);
//填充一个矩形
//语法: g.fillRect(int x, int y, int width, int height)
// 参数:x,y 设置矩形起始点的坐标,从坐标(0,0)原点开始
// 参数:width,height 设置矩形宽度与高度
g.fillRect(0,0,150,50);
//4.给矩形画一个边框
//设置画笔颜色为黑色
g.setColor(Color.black);
//画边框语法: g.drawRect(int x, int y, int width, int height)
// 参数:x,y 设置矩形起始点的坐标
// 参数:width,height 设置矩形宽度与高度
g.drawRect(1,1,147,47);
//5.画4条干扰线(防止扫描软件识别)
for (int i = 0; i < 4; i++) {
//确定一个随机颜色
g.setColor(getRandomColor());
//画干扰线语法: g.drawLine(int x1, int y1, int x2, int y2)
//确定随机的点的位置
int x1 = random.nextInt(146)+1; //1~146
int y1 = random.nextInt(46)+1; //1~46
int x2 = random.nextInt(146)+1; //1~146
int y2 = random.nextInt(46)+1; //1~46
//画线
g.drawLine(x1,y1,x2,y2);
}
//6.画4个随机生成的验证码字符串到图片上
//6.0 定义验证码生成的范围
String str = "zxcvbnmasdfghjkqwertyuipZXCVBNMASDFGHJKLQWERTYUP23456789传智教育";
//定义一个变量接收最后生成的4个随机字符,以后用于验证
StringBuilder checkCodeBuilder = new StringBuilder();
//6.1 随机生成4个验证码字符串
for (int i = 0; i < 4; i++) {
//随机生成一个位置
int index = random.nextInt(str.length());
//根据位置获取对应的字符
char c = str.charAt(index);
//将生成的字符拼接存储到checkCodeBuilder
checkCodeBuilder.append(c);
//设置画笔的字体大小
//g.setFont(Font font)
//Font构造函数语法:Font(String name, int style, int size)
// name是字体类型,比如微软雅黑
// style,设置加粗,Font.BOLD
// size,设置字体大小
g.setFont(new Font("微软雅黑",Font.BOLD,20));
//6.2 画到图片上
//画字符串语法:g.drawString(String str, int x, int y)
g.drawString(c+"",30+i*30,30);
}
//7.画完后,将内存图片给到输出流输出到浏览器上显示
// 语法:ImageIO.write(RenderedImage image,String format, OutputStream outputSteam)
// RenderedImage 是一个接口,实现类BufferedImage
ImageIO.write(image,"png",response.getOutputStream());
}
//定义获取一个随机颜色的方法
private Color getRandomColor(){
//创建颜色对象构造语法:new Color(int r, int g, int b)
//每个颜色值范围:0~255
int r = random.nextInt(256);
int g = random.nextInt(256);
int b = random.nextInt(256);
return new Color(r,g,b);
}
}
案例2、响应体-验证码切换以及滑动验证
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>登录页面</title>
<link href="css/bootstrap.min.css" rel="stylesheet">
<script src="js/jquery-3.2.1.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<style>
/* 滑块使用定位,背景没有设置宽度*/
.box {
width: 250px;
height: 50px;
background-color: #ccc;
position: relative;
margin: 0 auto;
}
.btn2 {
box-sizing: border-box;
width: 50px;
height: 50px;
border: 1px solid #ccc;
color: #ccc;
background-color: #fff;
position: absolute;
left: 0;
top: 0;
cursor: pointer;
z-index: 4;
}
.text {
position: absolute;
height: 50px;
left: 50%;
transform: translateX(-50%);
z-index: 2;
user-select: none;
padding-top: 15px;
}
.bg {
width: 0;
height: 50px;
background-color: #25c20f;
z-index: 3;
position: absolute;
top: 0;
left: 0;
}
</style>
</head>
<body>
<div class="container" style="max-width:400px">
<h3 style="text-align: center">用户登录</h3>
<form action="login" method="post" >
<div class="form-group">
<label for="name">用户名:</label>
<input type="text" name="name" class="form-control" id="name" placeholder="请输入用户名">
</div>
<div class="form-group">
<label for="password">密码:</label>
<input type="password" name="password" class="form-control" id="password" placeholder="请输入密码"/>
</div>
<div class="form-inline">
<label for="vcode">验证码:</label>
<input type="text" name="vcode" class="form-control" id="vcode" placeholder="验证码" style="width: 70px" maxlength="4"/>
<!--设置图片的src执行验证码图片输出的Servlet: CheckCodeServlet-->
<img src="CheckCodeServlet" style="width: 90px; height: 30px; cursor: pointer;" title="看不清,点击刷新" onclick="changeCheckCode(this)">
<script>
//验证码图片点击事件
function changeCheckCode(img){
//图标标签让浏览器重新加载src的资源的原理:就是src的值必须要变化,注意请求的资源CheckCodeServlet不能变
img.src = "CheckCodeServlet?time="+new Date().getTime();
}
</script>
</div>
<!--验证-->
<div class="box">
<!--滑块-->
<div class="btn2"></div>
<!--文字-->
<p class="text">请滑动滑块</p>
<!--背景-->
<div class="bg"></div>
</div>
<div style="text-align: center; padding-top: 20px;">
<input type="submit" value=" 登 录 " class="btn btn-primary" disabled="disabled"/>
</div>
</form>
</div>
<script>
function selector(name) {
return document.querySelector(name);
}
var box = selector('.box'),
btn2 = selector('.btn2'),
text = selector('.text'),
bg = selector('.bg'),
flag = false;
// 鼠标按下,移动,松开
// 按下的距离和移动的距离差就是滑块移动的距离
btn2.onmousedown = function (e) {//按钮按下的
var downX = e.clientX
btn2.onmousemove = function(e){//e 事件的状态
var moveX = e.clientX - downX;
if(moveX > 0) {
this.style.left = moveX + 'px';
bg.style.width = moveX + 'px'
// 滑块拉到头了,表示验证成功
if (moveX >= box.offsetWidth - this.offsetWidth) {
bg.style.zIndex = 1;// 设置bg的z-index的值是为了处理党滑块经过原始值的时候,bg将文字覆盖了。验证成功后,有让文字显示出来
text.innerText = '验证成功';
text.style.color = '#fff';
flag = true;
// 此时鼠标移动或者按下,滑块不在跟着移动了
btn2.onmousemove = null;//
btn2.onmousedown = null;//清除事件
/*
* 这里处理验证成功的操作
* */
document.querySelector(".btn").disabled = false;
}
}
}
}
btn2.onmouseup = function () {
btn2.onmousemove = null;
// 如果验证成功了,那就不会回到原点
if(flag){
return ;
}
this.style.left = 0;
bg.style.width = 0;
}
</script>
</body>
</html>