day14 --> (HTTP协议响应消息、Response对象、ServletContext对象)
HTTP协议:
1、请求消息:客户端发送给服务器端的数据
数据格式:
1.请求行
2.请求头
3.请求空行
4.请求体
2、响应消息:服务器端发送给客户端的数据
数据格式:
1.响应行
1、组成:协议 / 版本 响应状态码 状态码描述
2、响应状态码:服务器告诉客户端浏览器本次请求和响应的一个状态
状态码都是3位数字
分类:
1、1xx : 服务器接收客户端消息,但是没有接收完成,等待一段时间后,发送1xx状态码,“1开头的意思是...”
2、2xx : 成功。代表200
3、3xx : 重定向【资源跳转的一种方式】。
代表:302:
例如: 浏览器 --> 访问服务器资源A,A无法成功处理,返回给浏览器服务器资源C的地址,说:“我干不了,你让C去干吧~”,并给出本次响应状态位302。然后浏览器去找C
代表:304【访问缓存】:
例如:浏览器 --> 访问服务器资源a.png,因为图片这样的信息一般不会发生变化,浏览器的缓存中会保存这张图片a.png,如果第二次浏览器依然向服务器访问a.png,服务器会告诉浏览器说:“访问缓存吧”,并给出此次访问的状态码304。从而达到缓解服务器压力的目的
4、4xx : 客户端错误。
代表:404:
请求路径没有对应的资源
代表:405:
请求方式没有对应的 doGet 或者 doPost
5、5xx : 服务器错误,内部出现异常
代表:
500:服务器内部出现异常
2.响应头
1、格式:头名称 :值
2.常见的响应头:
1、Content - Type : 服务器告诉客户端本次响应体(HTML格式)数据格式以及编码格式
2、Content - disposition : 服务器告诉客户端以什么格式打开响应体数据
值:
in - line : 默认值,在当前页面内打开
attachment ; filename = xxx :以附件形式打开响应体。【文件下载】
3.响应空行
4.响应体
真实的,传输的数据
Response 对象
功能:设置响应消息
1. 设置响应行 :
1、格式:HTTP/1.1 200 ok
2、设置状态码:setStatus(int sc)
2.设置响应头 :
setHeader(String name, String value)
3.设置响应体 :
使用步骤:
1、获取输出流
字符输出流 : PrintWriter getWriter() 【打印流】
字节输出流 : ServletOutputStream getOutputStream() 【当作OutputStream使用】
2、使用输出流将数据输出到客户端浏览器
案例:
1.完成重定向

关键代码:
//1.设置状态码:302
response.setStatus(302);
//2.设置响应头location
response.setHeader("location","/day15/responseDemo2");
如果想要实现重定向的功能,设置状态码 和 设置响应头中的 location 都是固定的,只有 location 的值的有所变化的,因此以上代码可以被替换为:
response.sendRedirect("/day15/responseDemo2");
常见面试题:redirect 和 forward 的区别
重定向【redirect】的特点:
1.地址栏发生变化
2.重定向可以访问其他站点(其他服务器资源)
3.重定向是两次请求,不能使用 request 对象来共享数据
转发【forward】的特点:
1.转发地址栏路径不变
2.转发只能访问当前服务器下的资源
3.转发是一次请求
路径的写法:
1.路径分类:
1、相对路径 :通过相对路径不可以确定唯一资源
如:./index.html 【标识当前目录下】
不以斜杠开头,以 . 开头
规则:找到当前资源和目标资源之间的相对位置关系
./ : 标识当前目录,如果知识当前目录,那么 ./ 可以省略
../ : 标识上一级目录
2、【JSP常用】绝对路径:通过绝对路径可以确定唯一资源
如:http://localhost:8080/day15/responseDemo2 --> 绝对路径【省略协议、IP地址、端口号】:/day15/responseDemo2 ,以斜杠开头
规则:判断定义的路径是给 谁(浏览器 || 服务器) 用的?判断请求将来从哪发出
给浏览器用 :需要加 虚拟目录(项目的访问路径)【重定向√】
建议虚拟目录动态获取:request.getContextPath()
<a>、<form>、重定向
给服务器用: 不需要加 虚拟目录
转发路径
2.服务器输出字符数据到浏览器
步骤:
1、获取字符输出流
2、输出数据


乱码问题:
1.PrintWriter pw = response.getWriter(); 获取的流的默认编码是ISO-8859-1,即GBK
2.设置改流的默认编码
3.告诉浏览器响应体使用的编码
【注意:】在获取流之前设置编码:response.setContentType("text/html;charset=utf-8");
3.服务器输出字节数据到浏览器
4.验证码
@WebServlet("/checkCodeServlet")
public class CheckCodeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int width = 100;
int height = 50;
//1、创建一个对象,在内存中的图片
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
//2、美化图片
//2.1 填充背景色
Graphics graphics = image.getGraphics(); //获取画笔对象
//设置画笔颜色
graphics.setColor(Color.PINK);
//填充方框背景色
graphics.fillRect(0,0,width,height);
//2.2画边框
graphics.setColor(Color.BLUE);
graphics.drawRect(0,0,width-1,height-1);
String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
Character ch = null;
//2.3写验证码
for (int i = 1; i <= 4; i++) {
ch = str.charAt(random.nextInt(str.length()));
graphics.drawString(ch+"",i*20,25);
}
//2.4画干扰线
graphics.setColor(Color.GREEN);
graphics.drawLine(1,1,30,30);
int x1,y1,x2,y2;
for (int i = 1; i <= 10 ; i++) {
x1 = random.nextInt(width);
x2 = random.nextInt(width);
y1 = random.nextInt(height);
y2 = random.nextInt(height);
graphics.drawLine(x1,y1,x2,y2);
}
//2.3写验证码
// graphics.drawString("A",20,25);
// graphics.drawString("B",40,25);
// graphics.drawString("C",60,25);
// graphics.drawString("D",80,25);
//3、将图片输出到页面上展示
ImageIO.write(image,"jpg",response.getOutputStream());
}
}
<body>
<img id="checkCode" onclick="changeImg()" src="/day15/checkCodeServlet"/>
<br>
<hr>
<a href="">看不清?换一张</a>
</body>
<script>
function changeImg(){
let checkCode = document.getElementById("checkCode");
let date = new Date().getTime();//欺骗服务器这是新的请求,否则会加载缓存中的验证码,验证码不会改变
checkCode.src = "/day15/checkCodeServlet?" + date;
}
</script>
ServletContext对象
1.概念:代表整个web应用,可以和程序的容器(服务器)来通信
2.获取:
1、通过request对象获取
request.getServletContext();
2、通过HttpServlet获取
this.getServletContext();

访问后控制台打印:

ServletContext对象代表整个Web应用,而改Web应用只有一份,因为:两种不同的方式访问到的对象均为同一个
3.功能:
1、获取 MIME 类型:
MIME 类型:在互联网通信过程中定义的一种文件数据类型
格式:大类型 / 小类型;
例如:text / html --> 标识这是一个纯文本的、html格式的文件
image / jpeg
获取:String getMimeType(String file)

控制台打印:

2、域对象 :共享数据
1.setAttribute(String name,Object value)
2.getAttribute(String name)
3.removeAttribute(String name)
注:ServletContext【谨慎使用】 对象范围:所有用户所有请求数据
先访问:/servletContextDemo3:doPost

在访问: /servletContextDemo4:doPost

控制台打印输出:

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

方法【重要】:String getRealPath(String path)


控制台打印:

案例:
文件下载需求:
1、页面显示超链接
2、点击超链接后弹出下载提示框
3、完成图片文件下载
分析:
1.超链接指向的资源如果能够被浏览器解析,则在浏览器中展示,如果不能解析,则弹出下载提示框,不满足需求
2.任何资源都必须弹出下载提示框
3.使用响应头设置资源的打开方式:
content-disposit : attachment ; filename = xxx
步骤:
1.定义页面,编辑超链接 href 属性,指向Servlet,传递资源名称 filename
2.定义Servlet
1.获取文件名称
2.使用字节输入流加载文件进内存进内存
3.指定 response 的响应头
4.将数据写出到 response 输出流
问题:
中文文件名无法正常显示:
解决思路:
1.获取客户端使用的浏览器的版本信息
2.根据不同的版本信息,响应不同的数据,设置 filename 的编码方式不同
<body>
<a href="/day15/img/natural_view.jpeg">图片</a>
<hr/>
<a href="/day15/img/video1.mp4">视频</a>
<hr color="red"/>
<a href="/day15/downloadServlet?filename=自然风景.jpeg">图片</a>
<hr/>
<a href="/day15/downloadServlet?filename=video1.mp4">视频</a>
</body>
@WebServlet("/downloadServlet")
public class DownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1、获取请求参数,文件名称
String filename = request.getParameter("filename");
//2、使用字节输入流加载文件进内存
//2.1根据文件名找到文件的真实路径(即服务器路径)
ServletContext servletContext = this.getServletContext();
String realPath = servletContext.getRealPath("/img/" + filename);
//用字节流关联
FileInputStream fis = new FileInputStream(realPath);
//解决中文文件名问题:
//1、获取user-agent请求头
String agent = request.getHeader("user-agent");
//2、使用工具类方法编码文件名即可
filename = DownLoadUtils.getFileName(agent, filename);
//3、设置response响应头
//3.1设置响应头类型:content-type
String mimeType = servletContext.getMimeType(filename);
response.setHeader("content-type",mimeType);
//3.2设置响应头打开方式
response.setHeader("content-disposition","attachment;filename=" + filename);
//4将输入流的数据写出到输出流中
ServletOutputStream outputStream = response.getOutputStream();
byte[] buff = new byte[1024 * 8];
int len = 0;
while ((len = fis.read(buff)) != -1){
outputStream.write(buff,0,len);
}
}
}
工具类:
//import sun.misc.BASE64Encoder; //导入BASE64Encoder报错的
import java.util.Base64; //导入此包,【从网上查资料说是JDK版本不一致,JDK9之后更新为此包,】
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
public class DownLoadUtils {
public static String getFileName(String agent, String filename) throws UnsupportedEncodingException {
if (agent.contains("MSIE")) {
// IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
// 火狐浏览器
//BASE64Encoder base64Encoder = new BASE64Encoder();
Base64.Encoder base64 = Base64.getEncoder();
filename = "=?utf-8?B?" + base64.encode(filename.getBytes("utf-8")) + "?=";
} else {
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
}
浙公网安备 33010602011771号