一、HttpServletResponse
1.1、HttpServletResponse介绍
Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象、和代表响应的response对象。
- request:客户端请求过来的数据。
- response:服务端发送数据给客户端。
HttpServletResponse接口代表服务器的响应。这个对象中封装了向客户端发送数据、发送响应头,发送响应状态码的方法。
1、负责向客户端发送数据的相关方法
2、负责向客户端发送响应头和设置状态
除此之外,其中还定义一系列响应状态码常量:SC_NOT_FOUND(404)、SC_OK(200)、SC_INTERNAL_SERVER_ERROR(500)。
1.2、HttpServletResponse常用操作
1、使用OutputStream向客户端输出中文
public class ResponseDemo1 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String data = "中国"; response.getOutputStream().write(data.getBytes("UTF-8")); // 指定客户端(浏览器)以UTF-8编码打开 response.setHeader("content-type", "text/html;charset=UTF-8"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
2、使用PrintWriter向客户端输出中文
public class ResponseDemo1 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String data = "中国"; response.setCharacterEncoding("UTF-8"); // 获取打印流必须在response.setCharacterEncoding("UTF-8");之后 PrintWriter out = response.getWriter(); response.setHeader("content-type", "text/html;charset=UTF-8"); // 还可以使用这种方式 out.write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'/>"); out.write(data); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
注意:当需要向浏览器输出字符数据时,使用PrintWriter比较方便,省去了将字符转换成字节数组那一步。
3、使用PrintWriter向客户端输出数字
如果想让服务端输出什么,客户端就显示什么,需要保住传输的都是字符串。
public class ResponseDemo1 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setHeader("content-type", "text/html;charset=UTF-8"); ServletOutputStream outputStream = response.getOutputStream(); outputStream.write("使用OutputStream流输出数字1:".getBytes("UTF-8")); //outputStream.write(1); // 无法显示,因为只能输出字符串 outputStream.write((1+ "").getBytes()); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
使用PrintWriter打印数字也和OutputStream一样,需要转换成字符串输出。
4、文件下载
文件下载功能是web开发中经常使用到的功能,使用HttpServletResponse对象就可以实现文件的下载
文件下载功能的实现思路:
1.获取要下载的文件的绝对路径
2.获取要下载的文件名
3.设置content-disposition响应头控制浏览器以下载的形式打开文件
4.获取要下载的文件输入流
5.创建数据缓冲区
6.通过response对象获取OutputStream流
7.将FileInputStream流写入到buffer缓冲区
8.使用OutputStream将缓冲区的数据输出到客户端浏览器
public class ResponseDemo2 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { downloadFileByOutpustStream(response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } private void downloadFileByOutpustStream(HttpServletResponse response) throws IOException { // 1.获取要下载的文件的绝对路径 String realPath = this.getServletContext().getRealPath("/download/我的妹子.png"); // 2.获取要下载的文件名 String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1); // 3.设置content-disposition响应头控制浏览器以下载的形式打开文件 //response.setHeader("content-disposition", "attachment;filename=" + fileName); // 3.如果是中文文件名,则需要通过URLEncoder设置UTF-8编码 response.setHeader("content-disposition", "attachment;filename="+URLEncoder.encode(fileName, "UTF-8")); // 4.获取要下载的文件输入流 InputStream in = new FileInputStream(realPath); int len = 0; byte[] buf = new byte[1024]; OutputStream out = response.getOutputStream(); while((len = in.read(buf)) > 0){ out.write(buf, 0, len); } in.close(); } }
注意:在编写下载文件功能时,要使用OutputStream流,避免使用PrintWriter流,因为OutputStream流是字节流,可以处理任意类型的数据,而PrintWriter流是字符流,
只能处理字符数据,如果用字符流处理字节数据,会导致数据丢失。
5、生成图片验证码
生成图片主要用到了一个BufferedImage类,
public class ResponseDemo3 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置refresh响应头控制浏览器每隔3秒钟刷新一次 response.setHeader("refresh", "3"); // 1.在内存中创建一张图片 BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB); // 得到图片 //Graphics graphics = image.getGraphics(); Graphics2D graphics2d = (Graphics2D) image.getGraphics(); graphics2d.setColor(Color.WHITE);// 设置图片的背景色 graphics2d.fillRect(0, 0, 80, 20);// 填充背景色 // 3.向图片上写数据 graphics2d.setColor(Color.BLUE); graphics2d.setFont(new Font(null, Font.BOLD, 20)); graphics2d.drawString(makeNum(), 0, 20); // 4.设置响应头控制浏览器浏览器以图片的方式打开 response.setContentType("image/jpeg"); // 等同于response.setHeader("Content-Type", "image/jpeg"); // 5.设置响应头控制浏览器不缓存图片数据 response.setDateHeader("expries", -1); response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache"); // 6.将图片写给浏览器 ImageIO.write(image, "jpg", response.getOutputStream()); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } /** * 生成随机数字 * */ private String makeNum(){ Random random = new Random(); String num = random.nextInt(9999) + ""; StringBuffer sb = new StringBuffer(); for (int i = 0; i < 4 - num.length(); i++) { sb.append("0"); } num = sb.toString() + num; return num; } }
1.3、响应头常用操作
1、设置响应头禁止缓存当前内容
response.setDateHeader("expries", -1); response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache");
2、设置http响应头控制浏览器定时刷新网页(refresh)
response.setHeader("refresh", "3");
3、设置响应头控制浏览器浏览器以图片的方式打开
response.setContentType("image/jpeg");
或
response.setHeader("Content-Type", "image/jpeg");
4、Response请求重定向
public class ResponseDemo4 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /** * 第一种方式实现请求重定向 * */ response.setHeader("Location", "/JavaResponseDemo/index.jsp"); response.setStatus(HttpServletResponse.SC_FOUND);// 设置302状态码 /** * 第二种方式实现请求重定向 * 该方法内部包含: * response.setHeader("Location", "/JavaWeb_HttpServletResponse_Study_20140615/index.jsp"); * response.setStatus(HttpServletResponse.SC_FOUND);//设置302状态码,等同于response.setStatus(302); * */ response.sendRedirect("/JavaResponseDemo/index.jsp"); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
1.4、web工程中URL推荐写法
如果"/"是给服务器用的,则代表当前的web工程,如果"/"是给浏览器用的,则代表webapps目录。
1、"/"代表当前web工程的常见应用场景
a) 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");
b) 在服务器端forward到其他页面
/** * forward * 客户端请求某个web资源,服务器跳转到另外一个web资源,这个forward也是给服务器用的, * 那么这个"/"就是给服务器用的,所以此时"/"代表的就是web工程 */ this.getServletContext().getRequestDispatcher("/index.jsp").forward(request, response);
c) 使用include指令或者<jsp:include>标签引入页面
<%@include file="/jspfragments/head.jspf" %> 或 <jsp:include page="/jspfragments/demo.jsp" />
2、"/"代表webapps目录的常见应用场景
a) 使用sendRedirect实现请求重定向
response.sendRedirect("/JavaResponseDemo/index.jsp");
服务器发送一个URL地址给浏览器,浏览器拿到URL再去请求服务器,所以这个"/"是给浏览器使用的,此时"/"代表的就是webapps目录。
这种写法是将项目名称写死在程序中的做法,不灵活,我们可以这样写:
response.sendRedirect(request.getContextPath() + "/index.jsp");
b) 使用超链接跳转
<a href="/JavaResponseDemo/index.jsp">跳转到首页</a>
这是客户端浏览器使用的超链接跳转,这个"/"是给浏览器使用的,此时"/"代表的就是webapps目录。
使用超链接访问web资源,绝对路径的写法推荐使用下面的写法改进:
<a href="${pageContext.request.contextPath}/index.jsp">跳转到首页</a>
c) Form表单提交
<form action="/JavaResponseDemo/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>
d) js脚本和css样式文件的引用
<%--使用绝对路径的方式引用js脚本--%> <script type="text/javascript" src="${pageContext.request.contextPath}/js/index.js"></script> <%--${pageContext.request.contextPath}与request.getContextPath()写法是得到的效果是一样的--%> <script type="text/javascript" src="<%=request.getContextPath()%>/js/login.js"></script> <%--使用绝对路径的方式引用css样式--%> <link rel="stylesheet" href="${pageContext.request.contextPath}/css/index.css" type="text/css"/>
3、response细节问题
- getOutputStream和getWriter方法分别用于得到输出二进制数据、输出文本数据的ServletOuputStream、Printwriter对象。
- getOutputStream和getWriter这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一方法。
4、生成复杂图片验证码
a) 创建一个DrawImage Servlet,用来生成验证码图片代码参看附件:DrawImageServlet.rar
b) index.jsp的代码
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>验证码</title> </head> <body> <form action="#" method="post"> <img src="${pageContext.request.contextPath}/servlet/DrawImageServlet"/> </form> </body> </html>
运行效果如下: