0%

HttpServletResponse

JavaWeb学习笔记之HttpServletResponse

HttpServletResponse

Responses:问句应答;响应;回应;应答;及时作出反应。对浏览器的请求作出响应(反应)

简单分类

负责向浏览器发送数据的方法

1
2
3
ServletOutputStream getOutputStream() throws IOException;//字节输出流(二进制数据

PrintWriter getWriter() throws IOException;//字符输出流(文本数据

一般使用getOutputStream方法,有中文可以使用getWriter方法(避免乱码)。

负责向浏览器发送响应头的方法

1
2
3
4
5
void setCharacterEncoding(String var1);//设置编码类型

void setContentLength(int var1);//内容长度

void setContentType(String var1);//可以同时设置文件类型和编码类型

响应状态码,点这里复习一下

1
2
3
4
5
6
int SC_CONTINUE = 100;
int SC_OK = 200; //成功
int SC_SEE_OTHER = 303; //重定向
int SC_NOT_FOUND = 404; //找不到资源
int SC_INTERNAL_SERVER_ERROR = 500; //服务器错误
int SC_BAD_GATEWAY = 502; //网关错误

常见应用

  1. 向浏览器输出信息(response对象.getWriter().print(“要输出到浏览器的信息”)
  2. 下载文件
  3. 验证码实现
  4. 重定向(重要)

下载文件

  1. 要获取下载文件的路径
  2. 下载的文件名是啥?
  3. 设置响应头,让浏览器知道这是要下载文件
  4. 获取下载文件的输入流
  5. 创建缓冲区
  6. 获取OutputStream对象
  7. 将FileOutputStream流写入到buffer缓冲区
  8. 使用OutputStream将缓冲区中的数据输出到客户端
  9. 关闭流

代码演示

在resources文件夹内新建一个文件夹img,用于存放我们的图片资源。将一个名为xhr.gif的图片放在里边。

注意:若文件在classpath下,使用类加载器来查找资源,只需要知道文件名即可。但是如果文件不在classpath路径(/WEB-INF/classes/)下,需要指定相对路径。可以理解为类加载器的根目录就是classpath,所以我们需要在第6行代码的方法getResource()中填写img/xhr.gif

  • 查找文件
    • this.getServletContext().getClassLoader().getResource("img/xhr.gif").getPath();
  • 设置响应头
    • resp.setHeader("Content-disposition","attachment;filename="+ URLEncoder.encode(fileName,"utf-8"));
    • 这是告诉浏览器,现在传输的是用户要下载的文件
    • URLEncoder.encode(fileName,"utf-8"),这是对下载的文件的文件名进行编码,避免中文乱码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 要获取下载文件的路径
//在web应用程序中(因为使用了类加载器)查找文件xhr.gif,并返回绝对路径
String realPath = this.getServletContext().getClassLoader().getResource("img/xhr.gif").getPath();
System.out.println("下载文件的路径:"+realPath);
// 2. 下载的文件名是啥?(告诉浏览器)
String fileName = realPath.substring(realPath.lastIndexOf("/")+1);//从路径中截取文件名
// 3. 设置响应头,让浏览器知道这是要下载文件
resp.setHeader("Content-disposition","attachment;filename="+ URLEncoder.encode(fileName,"utf-8"));
// 4. 获取下载文件的输入流
FileInputStream filein = new FileInputStream(realPath);
// 5. 创建缓冲区
int len = 0;
byte[] buffer = new byte[1024];//相当于将文件拆分成一小块一小块的发送
// 6. 获取OutputStream对象
ServletOutputStream out = resp.getOutputStream();
// 7. 将FileOutputStream流写入到buffer缓冲区,使用OutputStream将缓冲区中的数据输出到客户端
while ((len = filein.read(buffer))>0){
out.write(buffer,0,len);//将整个缓冲区内的数据 发送到浏览器
}
// 8.关闭流
filein.close();
out.flush();
out.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);//post请求,也放到doGet方法中处理
}
}

别忘了注册servlet,这里FileServlet的请求路径配置为/down

别忘了配置Tomcat,若是该web应用程序配置过了则不需要重复配置。

运行结果:

验证码实现

验证码怎么来的?

  • 前端实现
  • 后端实现,需要用到Java的图片类,生成一个图片

案例应用,见这里:传送门

代码演示

方法getRandom,用于生成7位数的随机数。

1
2
3
4
5
6
7
8
9
10
//获取7位数的随机数
private String getRandom(){
String num = new Random().nextInt(1000000)+"";//取值范围是(包括前,不包括后):[0,n)
StringBuffer supplement = new StringBuffer();
for (int i = 1; i <= 7-num.length(); i++) {//如果生成的随机不是7位的,后边补0
supplement.append("0");
}
num = supplement.toString() + num;
return num;
}

重写方法doGet、doPost。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

//如何让浏览器3秒自动刷新一次
resp.setHeader("refresh","3");//3秒刷新一次
//在内存中创建一个图片
BufferedImage image = new BufferedImage(80,20,BufferedImage.TYPE_3BYTE_BGR);//宽、高、图片颜色类型
//绘制图片
Graphics2D g = (Graphics2D)image.getGraphics();//笔
//绘制背景颜色
g.setColor(Color.white);
g.fillRect(0,0,80,20);
//给图片写上验证码
g.setColor(Color.blue);
g.setFont(new Font(null,Font.BOLD,20));//设置字体大小
g.drawString(getRandom(),0,17);//绘制验证码
g.drawLine(0,4,80,13);//绘制两条干扰线
g.drawLine(0,12,80,10);

//告诉浏览器,这个请求用图片的方式打开
resp.setContentType("image/jpg");
//网站存在缓存,不让浏览器缓存(不占用浏览器资源)
resp.setDateHeader("expires",-1);
resp.setHeader("Cache-Control","no-cache");
resp.setHeader("Pragma","no-cache");

//把图片发送给浏览器
ImageIO.write(image,"jpg",resp.getOutputStream());


}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);//post请求,也放到doGet方法中处理
}
//获取7位数的随机数
private String getRandom(){
String num = new Random().nextInt(1000000)+"";//取值范围是(包括前,不包括后):[0,n)
StringBuffer supplement = new StringBuffer();
for (int i = 1; i <= 7-num.length(); i++) {//如果生成的随机不是7位的,后边补0
supplement.append("0");
}
num = supplement.toString() + num;
return num;
}

绘制验证码核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
//在内存中创建一个图片
BufferedImage image = new BufferedImage(80,20,BufferedImage.TYPE_3BYTE_BGR);//宽、高、图片颜色类型
//绘制图片
Graphics2D g = (Graphics2D)image.getGraphics();//笔
//绘制背景颜色
g.setColor(Color.white);
g.fillRect(0,0,80,20);
//给图片写上验证码
g.setColor(Color.blue);
g.setFont(new Font(null,Font.BOLD,20));//设置字体大小
g.drawString(getRandom(),0,17);//绘制验证码
g.drawLine(0,4,80,13);//绘制两条干扰线
g.drawLine(0,12,80,10);

需要注意方法drawString中的参数y。字符串的坐标中心点并不是左上角,看下图:

别忘了注册servlet,这里Servlet程序的请求路径配置为/img

别忘了配置Tomcat,若是该web应用程序配置过了则不需要重复配置。

运行结果:

扩展

客户端请求该Servlet程序时,带上一个参数timeout,代表验证码超时的时间,单位秒。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
int timeout = 30;//默认值;
if (req.getParameter("timeout") != null){
timeout = Integer.valueOf(req.getParameter("timeout"));
}
if (timeout <10 || timeout > 100){
return;//验证码超时时间,只能是10~100。不符合要求,直接退出程序
}
//生成验证码:
String vcode = getRandom();//生成随机 验证码
//添加验证码到Session中:
req.getSession().setAttribute("verificode",vcode);//若跨域,session无法保存验证码
//在验证码之间增加空格(为了美观)
StringBuffer stringBuffer=new StringBuffer();
for(int i=0;i<vcode.length();i++){
stringBuffer.append(vcode.charAt(i)+" ");
}

//在内存中创建一个图片:
BufferedImage image = new BufferedImage(80,20,BufferedImage.TYPE_3BYTE_BGR);//宽、高、图片颜色类型

//准备绘制图片:
Graphics2D g = (Graphics2D)image.getGraphics();//笔
//绘制背景颜色:
g.setColor(Color.white);//设置画笔颜色
g.fillRect(0,0,80,20);//起始x、y,结束x、y坐标。可以理解为宽、高为80*20

//绘制验证码:
g.setColor(Color.black);//设置画笔颜色,即验证码(数字)的颜色
g.setFont(new Font(null,Font.BOLD,15));//设置字体大小
g.drawString(vcode,0,17);//绘制验证码

//生成干扰线条:
for(int i=0;i<10;i++){
Random random=new Random();
int xBegin =random.nextInt(80);
int yBegin =random.nextInt(20);
int xEnd=random.nextInt(xBegin+30);
int yEnd=random.nextInt(yBegin+30);
g.setColor(getColor());//方法getColor,生成随机的颜色
g.drawLine(xBegin, yBegin, xEnd, yEnd);//绘制线条
}

resp.setContentType("image/jpg");//告诉浏览器,这个请求用图片的方式打开
ImageIO.write(image,"jpg",resp.getOutputStream());//把图片发送给浏览器

getColor方法:

1
2
3
4
5
6
7
8
//生成随机颜色
private Color getColor(){
Random random=new Random();
int r =random.nextInt(256);
int g =random.nextInt(256);
int b =random.nextInt(256);
return new Color(r,g,b);
}

跨域,session无法保存验证码。解决方法:

  1. 使用UUID类,生成一个唯一的uuid码。
    • 生成uuid码String uuid = UUID.randomUUID().toString();
  2. 将生成的验证码图片保存在服务器上,将验证码图片的路径和uuid码一块返回给客户端,并保存到ServletContext中。(可以用uuid给图片命名,这样只需要发送图片地址给客户端即可)
  3. 使用多线程定时或定时器类Timer(实现接口ActionListener中的actionPerformed()方法),在超时后将服务器上的验证码图片删掉
  4. 当客户端发送输入的验证码到服务器进行验证时,将验证码和uuid一块发过来
  5. 取出保存在ServletContext中的验证码和uuid,与客户端发过来的进行比对
  6. 具体实现案例,点这里

前端接收图片:

  • 第一种方式:后端以流的方式返回图片
  • 使用Ajax异步请求,更新id为verificode_img的img标签的src。
  • 使用window.URL.createObjectURL(服务器响应的图片流)将流转换为URL
  • jQuery不能接受流,为了解决跨域问题,只能使用原始的XHR。vue也可以实现接收流不过解决跨域有点麻烦。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function changecode(timeout){
let verificode_img = document.getElementById("verificode_img");
let xmlhttp;
if (window.XMLHttpRequest)
{
// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xmlhttp=new XMLHttpRequest();
}
else
{
// IE6, IE5 浏览器执行代码
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
var blob = xmlhttp.response;//将返回的数据放到二进制变量blob
verificode_img.onload = function() {
window.URL.revokeObjectURL(verificode_img.src);
};//通过blob将图片流加载到img中,由于blob太大会影响性能,应该在加载之后及时释放
verificode_img.src = window.URL.createObjectURL(blob);//将验证码图片添加到html标签中
}
}
xmlhttp.open("GET",myuri+"/verificode?timeout="+timeout,true);
xmlhttp.responseType = "blob";
xmlhttp.setRequestHeader("client_type", "DESKTOP_WEB");
xmlhttp.setRequestHeader("desktop_web_access_key", "_desktop_web_access_key");
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.send();
}
  • 第二种方式:后端返回图片URL
  • 使用Ajax异步请求,更新id为verificode_img的img标签的src。
  • 如果返回的URL相同,可能浏览器会使用本地的缓存图片,导致验证码图片不能及时更新。可以在src后边拼接一个随机参数。例如:?Math.random()
  • 参考文件上传案例将图片保存在服务器上,定时删掉图片(timeout超时时间),将图片的URL返回给客户端
  • 具体实现案例,点这里

重定向(重点)

  • 请求转发,浏览器地址栏路径不变
  • 路由重定向,浏览器地址栏路径会变
    • 常见场景:用户登陆
    • 重定向的地址,可能是站内资源也可能不是站内资源

1
void sendRedirect(String var1) throws IOException;

面试题:请你聊聊重定向和转发的区别?

相同点:

  • 页面都会实现跳转

不同点:

  • 请求转发,URL不会发生变化,状态码:307
  • 重定向,URL会发生变化,状态码:302

代码演示

注意:重定向是可以跳转到其他web应用程序的,所以重定向路径要带上web应用程序在Tomcat服务器中注册的请求路径。(就是在Tomcat服务器中部署web应用程序时设置的Application context)(访问在web.xml中注册了请求路径的servlet类时,不要带上web应用程序路径,因为会自动添加web应用程序路径进去)

  • 请求转发:映射的地址包括当前web应用程序路径(内部资源跳转)

  • 重定向:映射的地址不包括当前web应用程序路径

首页

改写web应用程序的首页,添加一个form表单。form表单的数据提交到路径当前web应用程序/login进行处理。

验证表单

类RequestTest,在网站配置文件web.xml中注册servlet时使用请求路径/login。用于处理form表单传过来的数据。使用HttpServletRequest对象的getParameter方法,获取过来的数据。重定向,使用的是HttpServletResponse对象的sendRedirect方法。重定向是可以跳转到其他web应用程序的,所以重定向路径要带上web应用程序在Tomcat服务器中注册的请求路径。(就是在Tomcat服务器中部署web应用程序时设置的Application context

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class RequestTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//处理请求
String username = req.getParameter("username");
String password = req.getParameter("password");

System.out.println("用户名:"+username+"\t密码:"+password);

resp.sendRedirect("/response_war/success.jsp");//重定向到success.jsp,response_war为应用程序在Tomcat中注册的路径
//重定向的时候一定要注意,路径问题,否则404
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);//post请求,也放到doGet方法中处理
}
}

重定向

模拟表单数据验证成功后,跳转到一个新的页面。新建一个jsp页面success.jsp,显示登陆成功。

运行结果

别忘了注册servlet,这里RequestTest的请求路径配置为/login

别忘了配置Tomcat,若是该web应用程序配置过了则不需要重复配置。

若图片不能正常显示,请在浏览器中打开

欢迎关注我的其它发布渠道