项目地址:https://github.com/zhangzeminzZ/ServletStudy
文档地址:HttpServletResponse
Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象、和代表响应的response对象。
request和response对象即然代表请求和响应,那我们要获取客户机提交过来的数据,只需要找request对象就行了。要向客户机输出数据,只需要找response对象就行了。
1.HttpServletResponse对象介绍
接口HttpServletResponse
实现的接口:ServletResponse
所有已知的实现类:
AddDefaultCharsetFilter.ResponseWrapper,CsrfPreventionFilter.CsrfResponseWrapper,ExpiresFilter.XHttpServletResponse,HttpServletResponseWrapper,Response,ResponseFacade,ResponseIncludeWrapper,ServletResponseWrapperInclude,Util.ImportResponseWrapper
扩展ServletResponse接口以在发送响应时提供特定于HTTP的功能。例如,它具有访问HTTP标头和cookie的方法。
servlet容器创建一个HttpServletResponse对象,并将其作为一个参数传递给servlet的服务方法(doGet,doPost等)。
HttpServletResponse对象代表服务器的响应。这个对象中封装了向客户端发送数据、发送响应头,发送响应状态码的方法。查看HttpServletResponse的API,可以看到这些相关的方法。
1.1 负责向客户端(浏览器)发送数据的相关方法
修饰符和类型 | 方法 | 说明 |
---|---|---|
ServletOutputStream | getOutputStream() | 返回一个ServletOutputStream适合在响应中写入二进制数据的对象。 |
java.io.PrintWriter | getWriter() | 返回PrintWriter可以将字符文本发送到客户端的对象。 |
1.2 负责向客户端(浏览器)发送响应头的相关方法
修饰符和类型 | 方法 | 说明 |
---|---|---|
void | addDateHeader(java.lang.String name, long date) | 添加具有给定名称和日期值的响应标头。 |
void | addHeader(java.lang.String name, java.lang.String value) | 添加具有给定名称和值的响应头。 |
void | addIntHeader | 添加具有给定名称和整数值的响应标头。 |
boolean | containsHeader(java.lang.String name) | 返回一个布尔值,指示是否已设置命名响应头。 |
void | setDateHeader(java.lang.String name, long date) | 设置具有给定名称和日期值的响应标头。 |
void | setHeader(java.lang.String name, java.lang.String value) | 用给定的名称和值设置响应头。 |
void | setIntHeader(java.lang.String name, int value) | 用给定的名称和整数值设置响应头。 |
1.3 负责向客户端(浏览器)发送响应状态码的相关方法
修饰符和类型 | 方法 | 说明 |
---|---|---|
void | setStatus(int sc) | 设置此响应的状态码。 |
1.4 响应状态码的常量
HttpServletResponse定义了很多状态码的常量(具体可以查看Servlet的API),当需要向客户端发送响应状态码时,可以使用这些常量,避免了直接写数字,常见的状态码对应的常量:
状态码404对应的常量
修饰符和类型 | 方法 | 说明 |
---|---|---|
static int | SC_NOT_FOUND | 指示请求的资源不可用的状态码(404)。 |
状态码200对应的常量
修饰符和类型 | 方法 | 说明 |
---|---|---|
static int | SC_OK | 指示请求成功的状态码(200)。 |
状态码500对应的常量
修饰符和类型 | 方法 | 说明 |
---|---|---|
static int | SC_INTERNAL_SERVER_ERROR | 状态代码(500),指示HTTP服务器内部发生错误,导致该服务器无法满足请求。 |
2.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的编码显示数据。
范例:使用OutputStream流向客户端浏览器输出"中国"这两个汉字
package com.zhangzemin;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
/**
* @author zhangzemin
* @date 2020/9/29 11:14
*/
public class ResponseDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
outputChineseByOutputStream(response);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
/**
* 使用OutputStream流输出中文
*
* @param response
* @throws IOException
*/
public void outputChineseByOutputStream(HttpServletResponse response) throws IOException {
/**
* 使用OutputStream输出中文注意问题:
* 在服务器端,数据是以哪个码表输出的,那么就要控制客户端浏览器以相应的码表打开,
* 比如:outputStream.write("中国".getBytes("UTF-8"));//使用OutputStream流向客户端浏览器输出中文,以UTF-8的编码进行输出
* 此时就要控制客户端浏览器以UTF-8的编码打开,否则显示的时候就会出现中文乱码,那么在服务器端如何控制客户端浏览器以以UTF-8的编码显示数据呢?
* 可以通过设置响应头控制浏览器的行为,例如:
* response.setHeader("content-type", "text/html;charset=UTF-8");//通过设置响应头控制浏览器以UTF-8的编码显示数据
*/
String data = "中国";
//获取OutputStream输出流
OutputStream outputStream = response.getOutputStream();
//通过设置响应头控制浏览器以UTF-8的编码显示数据,如果不加这句话,那么浏览器显示的将是乱码
response.setHeader("content-type", "text/html;charset=UTF-8");
/**
* data.getBytes()是一个将字符转换成字节数组的过程,这个过程中一定会去查码表,
* getBytes()方法如果不带参数,那么就会根据操作系统的语言环境来选择转换码表
* 如果是中文的操作系统环境,默认就是查找查GB2312的码表,
* 将字符转换成字节数组的过程就是将中文字符转换成GB2312的码表上对应的数字
* 比如:"中"在GB2312的码表上对应的数字是98,"国"在GB2312的码表上对应的数字是99
*/
//将字符转换成字节数组,指定以UTF-8编码进行转换
byte[] dataBytes = data.getBytes("UTF-8");
//使用OutputStream流向客户端输出字节数组
outputStream.write(dataBytes);
}
}
运行结果:
客户端浏览器接收到数据后,就按照响应头上设置的字符编码来解析数据。
2.2 使用PrintWriter流向客户端浏览器输出中文数据
使用PrintWriter流输出中文注意问题:
在获取PrintWriter输出流之前首先使用"response.setCharacterEncoding(charset)"设置字符以什么样的编码输出到浏览器,如:response.setCharacterEncoding("UTF-8");设置将字符以"UTF-8"编码输出到客户端浏览器,然后再使用response.getWriter();获取PrintWriter输出流,这两个步骤不能颠倒。
``
package com.zhangzemin;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
/**
* @author zhangzemin
* @date 2020/9/29 11:14
*/
public class ResponseDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
outputChineseByPrintWriter(response);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
/**
* 使用PrintWriter流输出中文
*
* @param response
*/
public void outputChineseByPrintWriter(HttpServletResponse response) throws IOException {
String data = "中国";
//设置将字符以"UTF-8"编码输出到客户端浏览器,如果不加则显示乱码
response.setCharacterEncoding("UTF-8");
//获取PrintWriter输出流
PrintWriter printWriter = response.getWriter();
printWriter.write(data);
}
}
当需要向浏览器输出字符数据时,使用PrintWriter比较方便,省去了将字符转换成字节数组那一步。
2.3 使用OutputStream或者PrintWriter向客户端浏览器输出数字
比如有如下的代码:
package com.zhangzemin;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
/**
* @author zhangzemin
* @date 2020/9/29 11:14
*/
public class ResponseDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
outputOneByOutputStream(response);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
/**
* 使用OutputStream流输出数字1
* @param response
* @throws IOException
*/
public void outputOneByOutputStream(HttpServletResponse response) throws IOException {
response.setHeader("content-type", "text/html;charset=UTF-8");
OutputStream outputStream = response.getOutputStream();
outputStream.write("使用OutputStream流输出数字1:".getBytes("UTF-8"));
outputStream.write(1);
}
}
运行上面代码显示的结果如下:
运行的结果和我们想象中的不一样,数字1没有输出来,下面我们修改一下上面的outputOneByOutputStream方法的代码,修改后的代码如下:
/**
* 使用OutputStream流输出数字1
* @param response
* @throws IOException
*/
public void outputOneByOutputStream(HttpServletResponse response) throws IOException {
response.setHeader("content-type", "text/html;charset=UTF-8");
OutputStream outputStream = response.getOutputStream();
outputStream.write("使用OutputStream流输出数字1:".getBytes("UTF-8"));
// outputStream.write(1);
outputStream.write((1+"").getBytes("UTF-8"));
}
1+""这一步是将数字1和一个空字符串相加,这样处理之后,数字1就变成了字符串1了,然后再将字符串1转换成字节数组使用O utputStream进行输出。
这次可以看到输出来的1了,这说明了一个问题:在开发过程中,如果希望服务器输出什么浏览器就能看到什么,那么在服务器端都要以字符串的形式进行输出。
如果使用PrintWriter流输出数字,那么也要先将数字转换成字符串后再输出,如下:
/**
* 使用PrintWriter流输出数字1
* @param response
* @throws IOException
*/
public void outputOneByPrintWriter(HttpServletResponse response) throws IOException {
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.write("使用PrintWriter流输出数字1:");
out.write(1+"");
}
2.4 文件下载
文件下载功能是web开发中经常使用到的功能,使用HttpServletResponse对象就可以实现文件的下载
文件下载功能的实现思路:
- 获取要下载的文件的绝对路径
- 获取要下载的文件名
- 设置content-disposition响应头控制浏览器以下载的形式打开文件
- 获取要下载的文件输入流
- .创建数据缓冲区
- 通过response对象获取OutputStream流
- 将FileInputStream流写入到buffer缓冲区
- 使用OutputStream将缓冲区的数据输出到客户端浏览器
2.4.1 使用Response实现文件下载
package com.zhangzemin;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
/**
* @author zhangzemin
* @date 2020/9/29 15:31
*/
public class ResponseDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
downloadFileByOutputStream(response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
/**
* 下载文件,通过OutputStream流
*
* @param response
*/
public void downloadFileByOutputStream(HttpServletResponse response) throws IOException {
//1.获取要下载的文件的绝对路径
String realPath = this.getServletContext().getRealPath("/photo/17.png");
//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();
out.close();
}
}
运行结果如下所示:
2.4.2 使用Response实现中文名文件下载
下载中文文件时,需要注意的地方就是中文文件名要使用URLEncoder.encode方法进行编码(URLEncoder.encode(fileName, "字符编码")),否则会出现文件名乱码。
package com.zhangzemin;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
/**
* @author zhangzemin
* @date 2020/9/29 15:31
*/
public class ResponseDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
downloadChineseFileByOutputStream(response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
/**
* 下载中文文件,中文文件下载时,文件名要经过URL编码,否则会出现文件名乱码
* @param response
* @throws IOException
*/
public void downloadChineseFileByOutputStream(HttpServletResponse response) throws IOException {
//1.获取要下载的文件的绝对路径
String realPath = this.getServletContext().getRealPath("/photo/测试.png");
//2.获取要下载的文件名
String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
//3.设置content-disposition响应头控制浏览器以下载的形式打开文件,中文文件名要使用URLEncoder.encode方法进行编码,否则会出现文件名乱码
response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
//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();
out.close();
}
}
运行结果如下所示:
2.4.3 使用PrintWriter流下载文件
package com.zhangzemin;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLEncoder;
/**
* @author zhangzemin
* @date 2020/9/29 15:31
*/
public class ResponseDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
downloadFileByPrintWriter(response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
public void downloadFileByPrintWriter(HttpServletResponse response) throws IOException {
//1.获取要下载的文件的绝对路径
String realPath = this.getServletContext().getRealPath("/photo/测试.png");
//2.获取要下载的文件名
String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
//3.设置content-disposition响应头控制浏览器以下载的形式打开文件,中文文件名要使用URLEncoder.encode方法进行编码,否则会出现文件名乱码
response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
//4.获取要下载的文件输入流
FileReader in = new FileReader(realPath);
int len = 0;
//5.创建数据缓冲区
char[] buffer = new char[1024];
//6.通过response对象获取OutputStream流
PrintWriter out = response.getWriter();
//7.将FileInputStream流写入到buffer缓冲区
while ((len = in.read(buffer)) > 0) {
//8.使用OutputStream将缓冲区的数据输出到客户端浏览器
out.write(buffer, 0, len);
}
in.close();
out.close();
}
}
运行结果如下图所示:
可是…
打开图片报错了…
这是为什么呢?
因为编写文件下载功能时推荐使用OutputStream流,避免使用PrintWriter流,因为OutputStream流是字节流,可以处理任意类型的数据,而PrintWriter流是字符流,只能处理字符数据,如果用字符流处理字节数据,会导致数据丢失。
下载之前的大小
下载之后的大小
这说明在下载的时候数据丢失了,所以下载不完全,所以这张图片虽然能够正常下载下来,但是却是无法打开的,因为丢失掉了部分数据
所以使用PrintWriter流处理字节数据,会导致数据丢失,这一点千万要注意,因此在编写下载文件功能时,要使用OutputStream流,避免使用PrintWriter流,因为OutputStream流是字节流,可以处理任意类型的数据,而PrintWriter流是字符流,只能处理字符数据,如果用字符流处理字节数据,会导致数据丢失。