五:HttpServletResponse对象
一、HttpServletResponse对象介绍
HttpServletResponse对象代表服务器的响应。这个对象中封装了向客户端发送数据、发送响应头,发送响应状态码的方法。查看HttpServletResponse的API,可以看到这些相关的方法。
1.1、负责向客户端(浏览器)发送数据的相关方法
1.2、负责向客户端(浏览器)发送响应头的相关方法
1.3、负责向客户端(浏览器)发送响应状态码的相关方法
1.4、响应状态码的常量
HttpServletResponse定义了很多状态码的常量(具体可以查看Servlet的API),当需要向客户端发送响应状态码时,可以使用这些常量,避免了直接写数字,常见的状态码对应的常量:
状态码404对应的常量
状态码200对应的常量
状态码500对应的常量
二、HttpServletResponse对象常见应用
2.1、使用OutputStream流向客户端浏览器输出中文数据
使用OutputStream流输出中文注意问题:
在服务器端,数据是以哪个码表输出的,那么就要控制客户端浏览器以相应的码表打开,比如:outputStream.write("中国".getBytes("UTF-8"));使用OutputStream流向客户端浏览器输出中文,以UTF-8的编码进行输出,此时就要控制客户端浏览器以UTF-8的编码打开,否则显示的时候就会出现中文乱码,那么在服务器端如何控制客户端浏览器以以UTF-8的编码显示数据呢?可以通过设置响应头控制浏览器的行为,例如:response.setHeader("content-type", "text/html;charset=UTF-8");通过设置响应头控制浏览器以UTF-8的编码显示数据。
2.2、使用PrintWriter流向客户端浏览器输出中文数据
使用PrintWriter流输出中文注意问题:
在获取PrintWriter输出流之前首先使用"response.setCharacterEncoding(charset)"设置字符以什么样的编码输出到浏览器,如:response.setCharacterEncoding("UTF-8");设置将字符以"UTF-8"编码输出到客户端浏览器,然后再使用response.getWriter();获取PrintWriter输出流,这两个步骤不能颠倒,如下:
1 response.setCharacterEncoding("UTF-8");//设置将字符以"UTF-8"编码输出到客户端浏览器 2 /** 3 * PrintWriter out = response.getWriter();这句代码必须放在response.setCharacterEncoding("UTF-8");之后 4 * 否则response.setCharacterEncoding("UTF-8")这行代码的设置将无效,浏览器显示的时候还是乱码 5 */ 6 PrintWriter out = response.getWriter();//获取PrintWriter输出流
注意:
1,getOutputStream()和getWriter()这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一方法。
2,getOutputStream()返回的字节输出流对象,类型为:ServletOutputStream,直接输出字节数组中的二进制数据。
3,getWriter()方法将Servlet引擎的数据缓冲区包装成PrintWriter类型的字符输出流对象后返回,PrintWriter对象只能输出字符文本内容。
然后再使用response.setHeader("content-type", "text/html;charset=字符编码");设置响应头,控制浏览器以指定的字符编码编码进行显示,例如:
1 //通过设置响应头控制浏览器以UTF-8的编码显示数据,如果不加这句话,那么浏览器显示的将是乱码 2 response.setHeader("content-type", "text/html;charset=UTF-8");
除了可以使用response.setHeader("content-type", "text/html;charset=字符编码");设置响应头来控制浏览器以指定的字符编码编码进行显示这种方式之外,还可以用如下的方式来模拟响应头的作用
1 /** 2 * 多学一招:使用HTML语言里面的<meta>标签来控制浏览器行为,模拟通过设置响应头控制浏览器行为 3 *response.getWriter().write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'/>"); 4 * 等同于response.setHeader("content-type", "text/html;charset=UTF-8"); 5 */ 6 response.getWriter().write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'/>");
2.4、文件下载
文件下载功能是web开发中经常使用到的功能,使用HttpServletResponse对象就可以实现文件的下载
文件下载功能的实现思路:
1.获取要下载的文件的绝对路径
2.获取要下载的文件名
3.设置content-disposition响应头控制浏览器以下载的形式打开文件
4.获取要下载的文件输入流
5.创建数据缓冲区
6.通过response对象获取OutputStream流
7.将FileInputStream流写入到buffer缓冲区
8.使用OutputStream将缓冲区的数据输出到客户端浏览器
package gacl.response.study; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.net.URLEncoder; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author gacl * 文件下载 */ public class ResponseDemo02 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { downloadFileByOutputStream(response);//下载文件,通过OutputStream流 } /** * 下载文件,通过OutputStream流 * @param response * @throws FileNotFoundException * @throws IOException */ private void downloadFileByOutputStream(HttpServletResponse response) throws FileNotFoundException, IOException { //1.获取要下载的文件的绝对路径 String realPath = this.getServletContext().getRealPath("/download/1.JPG"); //2.获取要下载的文件名 String fileName = realPath.substring(realPath.lastIndexOf("\\")+1); //3.设置content-disposition响应头控制浏览器以下载的形式打开文件 response.setHeader("content-disposition", "attachment;filename="+fileName); //4.获取要下载的文件输入流 InputStream in = new FileInputStream(realPath); int len = 0; //5.创建数据缓冲区 byte[] buffer = new byte[1024]; //6.通过response对象获取OutputStream流 OutputStream out = response.getOutputStream(); //7.将FileInputStream流写入到buffer缓冲区 while ((len = in.read(buffer)) > 0) { //8.使用OutputStream将缓冲区的数据输出到客户端浏览器 out.write(buffer,0,len); } in.close(); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
下载中文文件时,需要注意的地方就是中文文件名要使用URLEncoder.encode方法进行编码(URLEncoder.encode(fileName, "字符编码")),否则会出现文件名乱码。
2.5生成随机图片用作验证码
package gacl.response.study; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.Random; import javax.imageio.ImageIO; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 生成随机图片,用来作为验证码 */ public class DrawImage extends HttpServlet { private static final long serialVersionUID = 3038623696184546092L; public static final int WIDTH = 120;//生成的图片的宽度 public static final int HEIGHT = 30;//生成的图片的高度 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String createTypeFlag = request.getParameter("createTypeFlag");//接收客户端传递的createTypeFlag标识 //1.在内存中创建一张图片 BufferedImage bi = new BufferedImage(WIDTH, HEIGHT,BufferedImage.TYPE_INT_RGB); //2.得到图片 Graphics g = bi.getGraphics(); //3.设置图片的背影色 setBackGround(g); //4.设置图片的边框 setBorder(g); //5.在图片上画干扰线 drawRandomLine(g); //6.写在图片上随机数 //String random = drawRandomNum((Graphics2D) g,"ch");//生成中文验证码图片 //String random = drawRandomNum((Graphics2D) g,"nl");//生成数字和字母组合的验证码图片 //String random = drawRandomNum((Graphics2D) g,"n");//生成纯数字的验证码图片 //String random = drawRandomNum((Graphics2D) g,"l");//生成纯字母的验证码图片 String random = drawRandomNum((Graphics2D) g,createTypeFlag);//根据客户端传递的createTypeFlag标识生成验证码图片 //7.将随机数存在session中 request.getSession().setAttribute("checkcode", random); //8.设置响应头通知浏览器以图片的形式打开 response.setContentType("image/jpeg");//等同于response.setHeader("Content-Type", "image/jpeg"); //9.设置响应头控制浏览器不要缓存 response.setDateHeader("expries", -1); response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache"); //10.将图片写给浏览器 ImageIO.write(bi, "jpg", response.getOutputStream()); } /** * 设置图片的背景色 * @param g */ private void setBackGround(Graphics g) { // 设置颜色 g.setColor(Color.WHITE); // 填充区域 g.fillRect(0, 0, WIDTH, HEIGHT); } /** * 设置图片的边框 * @param g */ private void setBorder(Graphics g) { // 设置边框颜色 g.setColor(Color.BLUE); // 边框区域 g.drawRect(1, 1, WIDTH - 2, HEIGHT - 2); } /** * 在图片上画随机线条 * @param g */ private void drawRandomLine(Graphics g) { // 设置颜色 g.setColor(Color.GREEN); // 设置线条个数并画线 for (int i = 0; i < 5; i++) { int x1 = new Random().nextInt(WIDTH); int y1 = new Random().nextInt(HEIGHT); int x2 = new Random().nextInt(WIDTH); int y2 = new Random().nextInt(HEIGHT); g.drawLine(x1, y1, x2, y2); } } /** * 画随机字符 * @param g * @param createTypeFlag * @return * String... createTypeFlag是可变参数, * Java1.5增加了新特性:可变参数:适用于参数个数不确定,类型确定的情况,java把可变参数当做数组处理。注意:可变参数必须位于最后一项 */ private String drawRandomNum(Graphics2D g,String... createTypeFlag) { // 设置颜色 g.setColor(Color.RED); // 设置字体 g.setFont(new Font("宋体", Font.BOLD, 20)); //数字和字母的组合 String baseNumLetter = "0123456789ABCDEFGHJKLMNOPQRSTUVWXYZ"; //纯数字 String baseNum = "0123456789"; //纯字母 String baseLetter = "ABCDEFGHJKLMNOPQRSTUVWXYZ"; //createTypeFlag[0]==null表示没有传递参数 if (createTypeFlag.length > 0 && null != createTypeFlag[0]) { if (createTypeFlag[0].equals("ch")) { // 截取汉字 return createRandomChar(g, baseChineseChar); }else if (createTypeFlag[0].equals("nl")) { // 截取数字和字母的组合 return createRandomChar(g, baseNumLetter); }else if (createTypeFlag[0].equals("n")) { // 截取数字 return createRandomChar(g, baseNum); }else if (createTypeFlag[0].equals("l")) { // 截取字母 return createRandomChar(g, baseLetter); } }else { // 默认截取数字和字母的组合 return createRandomChar(g, baseNumLetter); } return ""; } /** * 创建随机字符 * @param g * @param baseChar * @return 随机字符 */ private String createRandomChar(Graphics2D g,String baseChar) { StringBuffer sb = new StringBuffer(); int x = 5; String ch =""; // 控制字数 for (int i = 0; i < 4; i++) { // 设置字体旋转角度 int degree = new Random().nextInt() % 30; ch = baseChar.charAt(new Random().nextInt(baseChar.length())) + ""; sb.append(ch); // 正向角度 g.rotate(degree * Math.PI / 180, x, 20); g.drawString(ch, x, 20); // 反向角度 g.rotate(-degree * Math.PI / 180, x, 20); x += 30; } return sb.toString(); } }
二、HttpServletResponse常见应用——设置响应头控制浏览器的行为
2.1、设置http响应头控制浏览器禁止缓存当前文档内容
1 response.setDateHeader("expries", -1); 2 response.setHeader("Cache-Control", "no-cache"); 3 response.setHeader("Pragma", "no-cache");
2.2、设置http响应头控制浏览器定时刷新网页(refresh)
1 response.setHeader("refresh", "5");//设置refresh响应头控制浏览器每隔5秒钟刷新一次
2.3、通过response实现请求重定向
请求重定向指:一个web资源收到客户端请求后,通知客户端去访问另外一个web资源,这称之为请求重定向。
应用场景:用户登陆,用户首先访问登录页面,登录成功后,就会跳转到某个页面,这个过程就是一个请求重定向的过程
实现方式:response.sendRedirect(String location),即调用response对象的sendRedirect方法实现请求重定向
sendRedirect内部的实现原理:使用response设置302状态码和设置location响应头实现重定向
2.2、使用PrintWriter流向客户端浏览器输出中文数据
使用PrintWriter流输出中文注意问题:
在获取PrintWriter输出流之前首先使用"response.setCharacterEncoding(charset)"设置字符以什么样的编码输出到浏览器,如:response.setCharacterEncoding("UTF-8");设置将字符以"UTF-8"编码输出到客户端浏览器,然后再使用response.getWriter();获取PrintWriter输出流,这两个步骤不能颠倒,如下:
1 response.setCharacterEncoding("UTF-8");//设置将字符以"UTF-8"编码输出到客户端浏览器 2 /** 3 * PrintWriter out = response.getWriter();这句代码必须放在response.setCharacterEncoding("UTF-8");之后 4 * 否则response.setCharacterEncoding("UTF-8")这行代码的设置将无效,浏览器显示的时候还是乱码 5 */ 6 PrintWriter out = response.getWriter();//获取PrintWriter输出流
然后再使用response.setHeader("content-type", "text/html;charset=字符编码");设置响应头,控制浏览器以指定的字符编码编码进行显示,例如:
1 //通过设置响应头控制浏览器以UTF-8的编码显示数据,如果不加这句话,那么浏览器显示的将是乱码 2 response.setHeader("content-type", "text/html;charset=UTF-8");
除了可以使用response.setHeader("content-type", "text/html;charset=字符编码");设置响应头来控制浏览器以指定的字符编码编码进行显示这种方式之外,还可以用如下的方式来模拟响应头的作用
1 /** 2 * 多学一招:使用HTML语言里面的<meta>标签来控制浏览器行为,模拟通过设置响应头控制浏览器行为 3 *response.getWriter().write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'/>"); 4 * 等同于response.setHeader("content-type", "text/html;charset=UTF-8"); 5 */ 6 response.getWriter().write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'/>");
四、web工程中URL地址的推荐写法
在JavaWeb开发中,只要是写URL地址,那么建议最好以/
开头,也就是使用相对路径的方式,那么这个/
到底代表什么呢?可以用如下的方式来记忆/
:如果"/"是给服务器用的,则代表当前的web工程,如果"/"是给浏览器用的,则代表webapps目录。
3.1、"/"代表当前web工程的常见应用场景
①.ServletContext.getRealPath(String path)获取资源的绝对路径
/** * 1.ServletContext.getRealPath("/download/1.JPG")是用来获取服务器上的某个资源, * 那么这个"/"就是给服务器用的,"/"此时代表的就是web工程 * ServletContext.getRealPath("/download/1.JPG")表示的就是读取web工程下的download文件夹中的1.JPG这个资源 * 只要明白了"/"代表的具体含义,就可以很快写出要访问的web资源的绝对路径 */ this.getServletContext().getRealPath("/download/1.JPG");
②.在服务器端forward到其他页面
/** * 2.forward * 客户端请求某个web资源,服务器跳转到另外一个web资源,这个forward也是给服务器用的, * 那么这个"/"就是给服务器用的,所以此时"/"代表的就是web工程 */ this.getServletContext().getRequestDispatcher("/index.jsp").forward(request, response);
③.使用include指令或者<jsp:include>标签引入页面
<%@include file="/jspfragments/head.jspf" %> <jsp:include page="/jspfragments/demo.jsp" />
此时"/"代表的都是web工程。
3.2、"/"代表webapps目录的常见应用场景
①.使用sendRedirect实现请求重定向
response.sendRedirect("/JavaWeb_HttpServletResponse_Study_20140615/index.jsp");
服务器发送一个URL地址给浏览器,浏览器拿到URL地址之后,再去请求服务器,所以这个"/"是给浏览器使用的,此时"/"代表的就是webapps目录,/JavaWeb_HttpServletResponse_Study_20140615/index.jsp
这个地址指的就是webapps\JavaWeb_HttpServletResponse_Study_20140615\index.jsp
response.sendRedirect("/项目名称/文件夹目录/页面");
这种写法是将项目名称写死在程序中的做法,不灵活,万一哪天项目名称变了,此时就得改程序,所以推荐使用下面的灵活写法:
将
response.sendRedirect("/JavaWeb_HttpServletResponse_Study_20140615/index.jsp");
这种写法改成
response.sendRedirect(request.getContextPath()+"/index.jsp");
request.getContextPath()
获取到的内容就是/JavaWeb_HttpServletResponse_Study_20140615
,这样就比较灵活了,使用request.getContextPath()
代替"/项目名称",推荐使用这种方式,灵活方便!
②.使用超链接跳转
<a href="/JavaWeb_HttpServletResponse_Study_20140615/index.jsp">跳转到首页</a>
这是客户端浏览器使用的超链接跳转,这个"/"是给浏览器使用的,此时"/"代表的就是webapps目录。
使用超链接访问web资源,绝对路径的写法推荐使用下面的写法改进:
<a href="${pageContext.request.contextPath}/index.jsp">跳转到首页</a>
这样就可以避免在路径中出现项目的名称,使用${pageContext.request.contextPath}取代"/JavaWeb_HttpServletResponse_Study_20140615"
③.Form表单提交
<form action="/JavaWeb_HttpServletResponse_Study_20140615/servlet/CheckServlet" method="post"> <input type="submit" value="提交"> </form>
这是客户端浏览器将form表单提交到服务器,所以这个"/"是给浏览器使用的,此时"/"代表的就是webapps目录。
对于form表单提交中action属性绝对路径的写法,也推荐使用如下的方式改进:
<form action="${pageContext.request.contextPath}/servlet/CheckServlet" method="post"> <input type="submit" value="提交"> </form>
${pageContext.request.contextPath}得到的就是"/JavaWeb_HttpServletResponse_Study_20140615"
${pageContext.request.contextPath}的效果等同于request.getContextPath(),两者获取到的都是"/项目名称"