服务器实际上是以web为根目录的,网站资源都是那里访问的,虚拟目录是设定的,因为为了把servlet类和web下的网站能看起来在同一个目录的效果,而@WebServlet的路径是绑定类的,也就是访问了该路径就是访问了该类。
- HTTP协议:响应消息
- Response对象
- ServletContext对象
02_HTTP_响应消息_概述
- 请求消息:客户端发送给服务器端的数据
- 数据格式:
- 请求行
- 请求头
- 请求空行
- 请求体
03_HTTP_响应消息_响应行_状态码
响应行:协议/版本 响应状态码 状态码描述
【重定向】302:客户端访问服务器资源A,A说没有,找C去;浏览器客户端很听话就去找C
【访问缓存】304:浏览器客户端在第一次访问服务器资源的时候,会把图片缓存到本地客户端。当客户端再一次向访问服务器的时候,服务器给客户端响应304说,你别再从我这里拿图片了,你本地已经有了。好处是:省时间,提效率
04_HTTP_响应消息_响应头
1、Content-Type::
什么个意思?
服务器告诉客户端应该以什么数据格式和什么编码格式去解析传输的数据。
比如,客户端请求打开网址,服务器做出响应Content-Type: text/html;charset=UTF-8。意思是,告诉客户端要以html文档的形式去解析并解析编码为UTF-8,而服务器要求客户端什么格式编码,客户端则会自动换成什么编码解析。
2、Content-disposition:
什么意思?服务器告诉客户端要以什么样的形式去打开【响应体】数据:
a)默认值:在当前页面打开。b)attachment;filename=xxx:文件下载
响应体
:传输数据的数据本身。
05_Response_功能介绍
字符输
出流只能输出字符的数据
ServletOutputStream:
可以当作OutputStream
来使用
案列汇总
06_Response_案例1_重定向_代码实现
分析:
重定向:实现步骤
- 设置302状态码
- 设置响应头location
- 自动跳转到另一个资源
Demo1
public class responseDemo1 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("我是Demo1...");
//访问responseDemo1的时候,会自动跳转到responseDemo2资源
//1、设置状态码302:告诉客户端重定向状态码
resp.setStatus(302);
//2、设置响应头【响应头名称(location)】+【资源路径】
resp.setHeader("location","/day15/responseDemo2");
//更简单方式
resp.sendRedirect("/day15/responseDemo2");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
}
Demo2
public class responseDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("我是Demo2.......");
}
}
07_重定向的特点
【面试】forward 和 redirect 区别
* 重定向的特点:redirect
1. 地址栏发生变化
2. 重定向可以访问其他站点(服务器)的资源
3. 重定向是两次请求。不能使用request对象来共享数据
* 转发的特点:forward
1. 转发地址栏路径不变
2. 转发只能访问当前服务器下的资源
3. 转发是一次请求,可以使用request对象来共享数据
重定向是两次请求。不能使用request对象来共享数据
与转发是一次请求,可以使用request对象来共享数据
要注意。
重定向怎么就2次请求?
资源跳转嘛,第一次请求一次,第二次请求一次。
08_相对路径
相对路径,相对相对,那是不是有个对于谁?第1种
第2种
09_相对路径&&动态获取虚拟目录
Demo:
2. 绝对路径:通过绝对路径可以确定唯一资源
* 如:http://localhost/day15/responseDemo2 /day15/responseDemo2【把IP地址和端口截取掉的路径】
* 以/开头的路径
* 规则:判断定义的路径是给谁用的?判断请求将来从哪儿发出
* 给客户端浏览器使用:需要加虚拟目录(项目的访问路径)
* 建议虚拟目录动态获取:request.getContextPath()
* <a> , <form> 重定向...
* 给服务器使用:不需要加虚拟目录
* 转发路径
对于绝对路径的规则的解析:
在定义路径的时候,要判断这个路径是给谁使用的(看谁请求也行,因为它要用,所以请求嘛)。给客户端就使用【虚拟目录】,给服务器就不需要【虚拟目录】
- 虚拟目录:<a>、form、重定向…
为什么重定向也是?因为重定向是服务器告诉状态码给客户端重定向的。即,给客户端使用。
- 不虚拟目录:请求转发。(服务器内部直接的资源跳转)
//访问/responseDemo1,会自动跳转到/responseDemo2资源
/* //1. 设置状态码为302
response.setStatus(302);
//2.设置响应头location
response.setHeader("location","/day15/responseDemo2");*/
request.setAttribute("msg","response");
//动态获取虚拟目录
String contextPath = request.getContextPath();
//简单的重定向方法
response.sendRedirect(contextPath+"/responseDemo2");
//response.sendRedirect("http://www.itcast.cn");
}
10_输出字符数据&&中文乱码问题
为什么会乱码?
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取流对象之前,要设置流的编码。服务器默认的编码:ISO-8859-1,所以设置GDK
// resp.setCharacterEncoding("GBK");
//【简化】告诉浏览器,服务器发送的消息体数据的编码。建议浏览器使用该编码解码
// resp.setHeader("content-type","text/html;charset=gdk");
//【超简化】设置编码
resp.setContentType("text/html;charset=gdk");
//获取字符流输出流
PrintWriter printWriter = resp.getWriter();
//输出数据到浏览器
printWriter.write("<a>你是谁啊?</a>");
}
}
11_输出字节数据
服务器输出字节数据到浏览器
* 步骤:
1. 获取字节输出流
2. 输出数据
@WebServlet("/responseDemo5")
public class ResponseDemo5 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
//1.获取字节输出流
ServletOutputStream sos = response.getOutputStream();
//2.输出数据(把字符串转换成字节数组)
sos.write("你好".getBytes("utf-8"));
}
12-验证码案例分析
4. 验证码
1. 本质:图片
2. 目的:防止恶意表单注册
验证码思路:
创建图片对象—>美化图片—>输出图片
模板:
package cn.itcast.web.servlet;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
@WebServlet("/checkCodeServlet")
public class CheckCodeServlet extends HttpServlet {
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 g = image.getGraphics();//画笔对象
g.setColor(Color.PINK);//设置画笔颜色
g.fillRect(0,0,width,height);
//2.2画边框
g.setColor(Color.BLUE);
g.drawRect(0,0,width - 1,height - 1);
String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789";
//生成随机角标
Random ran = new Random();
for (int i = 1; i <= 4; i++) {
int index = ran.nextInt(str.length());
//获取字符
char ch = str.charAt(index);//随机字符
//2.3写验证码
g.drawString(ch+"",width/5*i,height/2);
}
//2.4画干扰线
g.setColor(Color.GREEN);
//随机生成坐标点
for (int i = 0; i < 10; i++) {
int x1 = ran.nextInt(width);
int x2 = ran.nextInt(width);
int y1 = ran.nextInt(height);
int y2 = ran.nextInt(height);
g.drawLine(x1,y1,x2,y2);
}
//3.将图片输出到页面展示
ImageIO.write(image,"jpg",response.getOutputStream());
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
自写:
package cn.itcast.web.servlet;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Random;
@WebServlet("/CheckCodeServlet")
public class CheckCodeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int width = 100;//验证码宽
int height = 50;
//1、创建图对象,在内存中的图片(验证码图像对象)
BufferedImage img = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);//宽、高、图片样式
//2、美化图片
//2.1填充背景颜色
Graphics g = img.getGraphics();//画笔对象
g.setColor(Color.PINK);//设置画笔的颜色
g.fillRect(0,0,width,height);//填充颜色
//2.2画边框
g.setColor(Color.blue);//设置画笔颜色
g.drawRect(0,0,width-1,height-1);//画边框。-1是因为边框增加1px把另2个框挤出去了
//2.3画验证码
//生成随机角标。为了随机写验证码字符
String strs ="QWERTYUIOPASDFGHJKLZXCVBNM1234567890qwertyuiopasdfghjklzxcvbnm";
Random random = new Random();
for (int i = 1; i <= 4; i++) {
int index = random.nextInt(strs.length());//获取随机索引
char word = strs.charAt(index);//随机获取随机字符
g.drawString(word+"",width/5*i,height/2);//字符换字符串,(20,40,60,80),25宽(中间)
}
//2.4画干扰线
for (int i = 0; i < 10; i++) {
int x1 = random.nextInt(width);//2点确定一条直线
int y1 = random.nextInt(height);
int x2 = random.nextInt(width);//线的宽上限100
int y2 = random.nextInt(height);//线的高上限50
g.drawLine(x1,x2,y1,y2);
}
//3、把内存的图片写入页面
ImageIO.write(img,"jpg",resp.getOutputStream());//图片对象,图片格式,流
}
}
点击图片或者超链接更换验证码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
/*
分析:
点击超链接或者图片,需要换一张
1.给超链接和图片绑定单击事件
2.重新设置图片的src属性值
*/
window.onload = function () {
//1获取图片对象
var img = document.getElementById("checkcode");
//2、图片绑定点击事件
img.onclick = function(){
//3、获取时间戳(毫秒)
var time = new Date().getTime();
//4、设置图片属性
img.src ="/day15/CheckCodeServlet?"+time;
}
//超链接他同上
var img2= document.getElementById("change");
img2.onclick=function () {
var time = new Date().getTime();
img.src ="/day15/CheckCodeServlet?"+time;
}
}
</script>
</head>
<body>
<img id="checkcode" src="/day15/CheckCodeServlet" >
<a id="change" href="">看不清,重新换一张?</a>
</body>
</html>
15_ServletContext_概述
程序的容器是指。servlet直接的通信。
16_获取ServletContext对象
对象是一样的。
因为ServletContext代表整个web应用,一个整体。
17_ServletContext对象_获取MIME类型
MIME类型一般用在:
1、告诉浏览器你需要以什么类型什么格式去解析我的文件
2、浏览器请求服务器获取文件,服务器获取到MIME类型,并发给浏览器
小Demo:
ServletContext功能:
1. 获取MIME类型:
* MIME类型:在互联网通信过程中定义的一种文件数据类型
* 格式: 大类型/小类型 text/html image/jpeg
* 获取:String getMimeType(String file)
2. 域对象:共享数据
3. 获取文件的真实(服务器)路径
*/
//2. 通过HttpServlet获取
ServletContext context = this.getServletContext();
//3. 定义文件名称
String filename = "a.jpg";//image/jpeg
//4.获取MIME类型
String mimeType = context.getMimeType(filename);
System.out.println(mimeType);
传入"a.jpg"到context.getMimeType,web.xml会自动匹配成img/jpg,并返回
输出:image/jpg
18_ServletContext对象_域对象
ServletContext对象的与对象很广,整个web程序嘛。
比如,资源A存储域对象数据,资源B不需要什么资源跳转,直接可以获取与对象的数据。但是,但是,一般这个比较谨慎使用。
为什么?
1、不安全。范围大,也就是说所有的用户都可以访问和操控它,这样不安全
2、ServletContext对象的域对象声明周期很长。一旦创建它是随着服务器的关闭而毁灭,如果创建的数量多了,服务器容易负担中
19_ServletContext_功能_获取文件服务器路径
3. 获取文件的真实(服务器)路径
1. 方法:String getRealPath(String path)
注意:
1、IDEA左侧看到的目录情况和Tomcat服务器部署web项目的目录会有点不一样。Tomcat服务器部署web项目会把src目录放进这个WEB-INF里面。classses差不多是src
2、classLoader加载器可以获取src目录下的文件真实路径,但是局限性是不能够获取web里面的文件真实路径
20、文件下载案例分析。
搞一个servlet资源,专门写响应头告诉客户端要以附件的形式打开文件。
21、文件下载代码实现。
DIY代码:
@WebServlet("/downloadServlet")
public class DownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、获取请求参数,文件名称
String filename = req.getParameter("filename");
System.out.println("路径1:"+filename);
//2、使用字节输入流加载文件进内存。(读取文件)
//2.1 找到文件服务器路径
ServletContext servletContext = this.getServletContext();
String contextPath = servletContext.getRealPath("/img/"+filename);
System.out.println("路径2:"+contextPath);
//2.2 用字节流关联文件(读取文件)
FileInputStream fs = new FileInputStream(new File(contextPath));
//3、设置响应头
//3.1 设置响应类型:content-type
String mimeType = servletContext.getMimeType(filename);//获取文件的MIME类型
System.out.println("获取文件类型:"+mimeType);
resp.setHeader("content-type",mimeType);
//3.2 设置打开方式的响应头:content-disposition
resp.setHeader("content-disposition","attachment;filename="+filename);
//4、将输入流的数据写出到输出流中(复制:边读边写)
ServletOutputStream os = resp.getOutputStream();
byte[] bytes = new byte[1024 * 8];
int len = 0;
while ((len=fs.read(bytes))!=-1){
os.write(bytes,0,len);
}
System.out.println("获取有效字节"+len);
fs.close();
}
}
不知道什么原因,我的代码是写对的,但是,除了能够下载img格式的文件其他的类型的文件都不行。如果是下载其他文件,它没有执行到获取MIME类型那里,System.out.println("获取文件类型:"+mimeType);
22、解决中文乱码
使用场景:
表单请求发送地址,服务器截取”九尾.jpg“并读取本地的”九尾.jpg“文件,写出给浏览器的”九尾.jpg“中的”九尾“进行了url编码给浏览器自动解析。
为什么会乱码?
因为每个浏览器的默认编码是不一样的,我们要根据不同的浏览器版本来设置不一样编码格式。
借助此DownLoadUtils.java(复制粘贴进utlis包就好了)
此类把功能都包装起来了。看源代码:
【看这里很重要】:把获取的请求参数值”九尾.jpg“(filename头=参数值)进行url编码,url编码的数据浏览器才会解析的到
加这个代码:
//解决中文乱码问题(在写出之前设置,)
String agent = req.getHeader("user-agent");
filename = DownLoadUtils.getFileName(agent, filename);//返回一个编码后的filename
但是import sun.misc.BASE64Encoder在JDK9之后被移除了,你无法在本地的JDK导入这个包。
完整模板:
package cn.itcast.web.download;
import cn.itcast.web.utils.DownLoadUtils;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
@WebServlet("/downloadServlet")
public class DownloadServlet extends HttpServlet {
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);
//2.2用字节流关联
FileInputStream fis = new FileInputStream(realPath);
//3.设置response的响应头
//3.1设置响应头类型:content-type
String mimeType = servletContext.getMimeType(filename);//获取文件的mime类型
response.setHeader("content-type",mimeType);
//3.2设置响应头打开方式:content-disposition
//解决中文文件名问题
//1.获取user-agent请求头、
String agent = request.getHeader("user-agent");
//2.使用工具类方法编码文件名即可
filename = DownLoadUtils.getFileName(agent, filename);
response.setHeader("content-disposition","attachment;filename="+filename);
//4.将输入流的数据写出到输出流中
ServletOutputStream sos = response.getOutputStream();
byte[] buff = new byte[1024 * 8];
int len = 0;
while((len = fis.read(buff)) != -1){
sos.write(buff,0,len);
}
fis.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}