一、基本介绍
1、概念
Filter也称之为过滤器,通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截。
2、原理图
注意:filter在web程序启动的时候被创建(和servlet相似)、init()方法也只在启动的时候调用一次。
二、技术详情
1、入门案例
1.编写Filter
public class FilterDemo1 implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("filterdemo1执行前!!!");//部分1
chain.doFilter(request, response); //让目标资源执行,放行 部分2
System.out.println("filterdemo1执行后!!!"); //部分3
}
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
public void destroy() {
// TODO Auto-generated method stub
}
}
注意:假如该过滤器拦截index.jsp,如果没有部分2,那么index.jsp是不会执行的
说明:在部分1中可以先对request、response进行加工,来对多个资源进行处理,如解决乱码问题(这样所以的servlet都解决了)
//对request和response进行一些预处理
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
在部分2中可以返回执行的结果,如果下一层(servlet、filter...)还没有执行完的话,2是不会执行的(参考下)
2.配置Filter
在web.xml文件中
<filter>
<filter-name>FilterDemo1</filter-name>
<filter-class>com.shen.web.filter.FilterDemo1</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterDemo1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
注意:这里拦截所有的url(参考servlet),即所有的资源都要先通过FilterDemo1过滤器
2、多重Filter(Filter链)
假如现在有两个filter
1.配置文件
<filter>
<filter-name>FilterDemo1</filter-name>
<filter-class>com.shen.web.filter.FilterDemo1</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterDemo1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>FilterDemo2</filter-name>
<filter-class>com.shen.web.filter.FilterDemo2</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterDemo2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
注意:谁个在前面谁个先执行(FilterDemo1先执行)
2.程序
FilterDemo1:
System.out.println("filterdemo1执行前!!!");
chain.doFilter(request, response); //让目标资源执行,放行
System.out.println("filterdemo1执行后!!!");
FilterDemo2:
System.out.println("filterdemo2执行前~!!");
chain.doFilter(request, response);
System.out.println("filterdemo2执行后~!!");
ServletDemo1:
System.out.println("servletdemo1");
request.getRequestDispatcher("/index.jsp").forward(request, response);
3.结果
filterdemo1执行前! filterdemo2执行前! servletdemo1 filterdemo2执行后! filterdemo1执行后! |
3、初始化参数
1.配置
<filter>
<filter-name>FilterDemo3</filter-name>
<filter-class>com.shen.web.filter.FilterDemo3</filter-class>
<init-param>
<param-name>xxx</param-name>
<param-value>yyy</param-value>
</init-param>
</filter>
2.获取
public class FilterDemo3 implements Filter {
private FilterConfig config = null;//应为初始化在init中
public void init(FilterConfig filterConfig) throws ServletException {
this.config = filterConfig;
}
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//获取配置的初始化参数
System.out.println(this.config.getInitParameter("xxx"));
}
}
附:所有获取方式
- String getFilterName():得到filter的名称。
- String getInitParameter(String name): 返回在部署描述中指定名称的初始化参数的值。如果不存在返回null.
- Enumeration getInitParameterNames():返回过滤器的所有初始化参数的名字的枚举集合。
- public ServletContext getServletContext():返回Servlet上下文对象的引用。
4、配置拦截类型
<filter>
<filter-name>FilterDemo4</filter-name>
<filter-class>com.shen.web.filter.FilterDemo4</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterDemo4</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>ERROR</dispatcher><!--在这里指定拦截的方式-->
</filter-mapping>
说明:获取页面(error.jsp),只有在以错误处理的方式获取时,才会拦截,如果直接以http://local.../error.jsp 方式来访问时,是不会被拦截的。
其他类型:
REQUEST | 当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用 |
INCLUDE | 如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用 |
FORWARD | 如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用 |
ERROR | 如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用 |
注意:默认是REQUEST
三、常见应用
1、禁止缓存所有动态页面
配置:
<filter>
<filter-name>NoCacheFilter</filter-name>
<filter-class>com.shen.web.filter.NoCacheFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>NoCacheFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
代码:
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
response.setDateHeader("Expires", -1);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
chain.doFilter(request, response);
}
2、缓存页面中的静态资源
配置:
<filter-mapping>
<filter-name>AutoLoginFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>ExpiresFilter</filter-name>
<filter-class>com.shen.web.filter.ExpiresFilter</filter-class>
<init-param>
<param-name>css</param-name>
<param-value>4</param-value><!--缓存时间为4小时-->
</init-param>
<init-param>
<param-name>jpg</param-name>
<param-value>1</param-value>
</init-param>
<init-param>
<param-name>js</param-name>
<param-value>4</param-value>
</init-param>
<init-param>
<param-name>bmp</param-name>
<param-value>4</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>ExpiresFilter</filter-name>
<url-pattern>*.jpg</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>ExpiresFilter</filter-name>
<url-pattern>*.css</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>ExpiresFilter</filter-name>
<url-pattern>*.js</url-pattern>
</filter-mapping>
代码:
//控制缓存的filter
public class ExpiresFilter implements Filter {
private FilterConfig filterConfig;
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//1.获取用户想访问的资源
String uri = request.getRequestURI();
//2.得到用户想访问的资源的后缀名
String ext = uri.substring(uri.lastIndexOf(".")+1);
//得到资源需要缓存的时间
String time = filterConfig.getInitParameter(ext);
if(time!=null){
long t = Long.parseLong(time)*3600*1000;
response.setDateHeader("expires", System.currentTimeMillis() + t);
}
chain.doFilter(request, response);
}
3、实现用户自动登陆
配置:
<filter>
<filter-name>AutoLoginFilter</filter-name>
<filter-class>com.shen.web.filter.AutoLoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AutoLoginFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
页面:
登陆::
<form action="${pageContext.request.contextPath }/servlet/LoginServlet" method="post">
用户名:<input type="text" name="username"><br/>
密码:<input type="password" name="password"><br/>
有效期:
<input type="radio" name="logintime" value="3600">1小时
<input type="radio" name="logintime" value="${10*60 }">10分钟
<input type="radio" name="logintime" value="${5*60 }">5分钟
<br/>
<input type="submit" value="登陆">
</form>
显示::(message)
${message }
代码:
实体::(user)
public class User {
private String username;
private String password;
//get和set和构造函数
}
数据库(模拟)::(MyDb)
public class MyDb {
private static List list = new ArrayList();
static{
list.add(new User("aaa","123"));
list.add(new User("bbb","123"));
list.add(new User("ccc","123"));
}
public static List getAll(){
return list;
}
}
注意:密码在这里存的是明文
登陆::(LoginServlet)
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
UserDao dao = new UserDao();
User user = dao.find(username, password);
if(user==null){
request.setAttribute("message", "用户名或密码不对!!");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}
request.getSession().setAttribute("user", user);
request.setAttribute("message", "恭喜,登陆成功!!");
//发送自动登陆cookie
sendAutoLoginCookie(request,response,user);
request.getRequestDispatcher("/message.jsp").forward(request, response);
}
private void sendAutoLoginCookie(HttpServletRequest request, HttpServletResponse response, User user) {
int logintime = Integer.parseInt(request.getParameter("logintime"));
Cookie cookie = new Cookie("autologin",user.getUsername() + "." + WebUtils.md5(user.getPassword()));//“.”为分割符【md5加密后不产生.】,autologin="username.password"
cookie.setMaxAge(logintime);
cookie.setPath("/day18");
response.addCookie(cookie);
}
注意:存给cookie的是md5加密后的
Md5加密:WebUtils
public class WebUtils {
public static String md5(String message) {
try {
MessageDigest md = MessageDigest.getInstance("md5");
byte result[] = md.digest(message.getBytes());
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(result);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
}
过滤器::(AutoLoginFilter)
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
if(request.getSession().getAttribute("user")!=null){//如果用户已经登陆,直接放行
chain.doFilter(request, response);
return;
}
//1.得到用户带过来的authlogin的cookie,
String value = null;
Cookie cookies[] = request.getCookies();
for(int i=0;cookies!=null && i<cookies.length;i++){
if(cookies[i].getName().equals("autologin")){
value = cookies[i].getValue();
}
}
//2.得到 cookie中的用户名和密码
if(value!=null){
String username = value.split("\\.")[0];
String password = value.split("\\.")[1];
//3.调用dao获取用户对应的密码
UserDao dao = new UserDao();
User user = dao.find(username);
String dbpassword = user.getPassword();
//4.检查用户带过来的md5的密码和数据库中的密码是否匹配,如匹配则自动登陆
if(password.equals(WebUtils.md5(dbpassword))){
request.getSession().setAttribute("user", user);
}
}
chain.doFilter(request, response);
}
4、解决全站乱码问题
配置:
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.shen.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>charset</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
代码:
//解决全站乱码问题
public class CharacterEncodingFilter implements Filter {
private FilterConfig filterConfig = null;
private String defaultCharset = "UTF-8";//设置一个默认值
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
String charset = filterConfig.getInitParameter("charset");
if(charset==null){
charset = defaultCharset;
}
request.setCharacterEncoding(charset); //其实只能解决post方式
response.setCharacterEncoding(charset);
response.setContentType("text/html;charset=" + charset);
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
public void destroy() {
// TODO Auto-generated method stub
}
}
注意:以上代码实际上只能解决post方式的请求,而不能解决get方式。
5、对request进行增强
1.解决全站乱码
1.包装设计
如果想要解决get方式请求,需要采用包装类的设计思想(将request进行增强):
- 实现与被增强对象相同的接口
- 定义一个变量记住被增强对象
- 定义一个构造器,接收被增强对象
- 覆盖需要增强的方法
- 对于不想增强的方法,直接调用被增强对象(目标对象)的方法
//为解决全站乱码
public class CharacterEncodingFilter implements Filter {
public void destroy() {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
request.setCharacterEncoding("gb2312");
response.setCharacterEncoding("gb2312");
response.setContentType("text/html;charset=gb2312");
MyCharacterEncodingRequest requestWrapper = new MyCharacterEncodingRequest(request);
chain.doFilter(requestWrapper, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
}
class MyCharacterEncodingRequest extends HttpServletRequestWrapper{
private HttpServletRequest request;
public MyCharacterEncodingRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public String getParameter(String name) {
try{
String value= this.request.getParameter(name);
if(value==null){
return null;
}
if(!this.request.getMethod().equalsIgnoreCase("get")) {
return value;
}
value = new String(value.getBytes("ISO8859-1"),this.request.getCharacterEncoding());
return value;
}catch (Exception e) {
throw new RuntimeException(e);
}
}
}
2.动态代理
public class CharacterEncodingFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
request.setCharacterEncoding("UTF-8"); //post
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
//request.getParamter() requestProxy.getParameter()
chain.doFilter((ServletRequest) Proxy.newProxyInstance(CharacterEncodingFilter.class.getClassLoader(),request.getClass().getInterfaces(), new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(!method.getName().equals("getParameter")){
return method.invoke(request, args);
}
if(!request.getMethod().equalsIgnoreCase("get")){
return method.invoke(request, args);
}
//getParameter get
String value = (String) method.invoke(request, args);
if(value==null){
return null;
}
return new String(value.getBytes("iso8859-1"),"UTF-8");
}
}), response);
}
public void destroy() {}
public void init(FilterConfig filterConfig) throws ServletException {}
}
2.对脏话进行处理
//脏话过滤器
public class DirtyFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
DirtyRequest dirtyrequest = new DirtyRequest(request);
chain.doFilter(dirtyrequest, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
}
public void destroy() {
}
}
class DirtyRequest extends HttpServletRequestWrapper{
private List<String> dirtyWords = Arrays.asList("傻B","操蛋","畜生");
private HttpServletRequest request;
public DirtyRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public String getParameter(String name) {
String value = this.request.getParameter(name);
if(value==null){
return null;
}
for(String dirtyWord : dirtyWords){
if(value.contains(dirtyWord)){
value = value.replace(dirtyWord, "****");
}
}
return value;
}
}
3.对HTML元素进行处理
//html转义过滤器
public class HtmlFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
MyHtmlRequest myrequest = new MyHtmlRequest(request);
chain.doFilter(myrequest, response);
}
public void destroy() {
// TODO Auto-generated method stub
}
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
}
class MyHtmlRequest extends HttpServletRequestWrapper{
private HttpServletRequest request;
public MyHtmlRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public String getParameter(String name) {
String value = this.request.getParameter(name);
if(value==null){
return null;
}
return filter(value);
}
public String filter(String message) {
if (message == null)
return (null);
char content[] = new char[message.length()];
message.getChars(0, message.length(), content, 0);
StringBuffer result = new StringBuffer(content.length + 50);
for (int i = 0; i < content.length; i++) {
switch (content[i]) {
case '<':
result.append("<");
break;
case '>':
result.append(">");
break;
case '&':
result.append("&");
break;
case '"':
result.append(""");
break;
default:
result.append(content[i]);
}
}
return (result.toString());
}
}
6、对response进行增强
1.全站压缩
1.包装设计模式
//解决全站压缩
public class GzipFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
BufferResponse myresponse = new BufferResponse(response);
chain.doFilter(request, myresponse);
//拿出缓存中的数据,压缩后再打给浏览器
byte out[] = myresponse.getBuffer();
System.out.println("原始大小:" + out.length);
ByteArrayOutputStream bout = new ByteArrayOutputStream();
GZIPOutputStream gout = new GZIPOutputStream(bout);
gout.write(out);
gout.close();
byte gzip[] = bout.toByteArray();
System.out.println("压缩后的大小:" + gzip.length);
response.setHeader("content-encoding", "gzip");
response.setContentLength(gzip.length);
response.getOutputStream().write(gzip);
}
public void destroy() {
}
public void init(FilterConfig filterConfig) throws ServletException {
}
}
class BufferResponse extends HttpServletResponseWrapper{
private ByteArrayOutputStream bout = new ByteArrayOutputStream();
private PrintWriter pw;
private HttpServletResponse response;
public BufferResponse(HttpServletResponse response) {
super(response);
this.response = response;
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return new MyServletOutputStream(bout);
}
@Override
public PrintWriter getWriter() throws IOException {
pw = new PrintWriter(new OutputStreamWriter(bout,this.response.getCharacterEncoding())); //PrintWriter.write(中国) <br>,要指定码表,不然可能乱码!!!!!!!!!!!!
return pw;
}
public byte[] getBuffer(){
try{
if(pw!=null){
pw.close();
}
if(bout!=null){
bout.flush();
return bout.toByteArray();
}
return null;
}catch (Exception e) {
throw new RuntimeException(e);
}
}
}
class MyServletOutputStream extends ServletOutputStream{
private ByteArrayOutputStream bout;
public MyServletOutputStream(ByteArrayOutputStream bout){
this.bout = bout;
}
@Override
public void write(int b) throws IOException {
this.bout.write(b);
}
}
2.动态代理技术
public class GzipFilter implements Filter {
public void destroy() {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) resp;
final ByteArrayOutputStream bout = new ByteArrayOutputStream();
final PrintWriter pw = new PrintWriter(new OutputStreamWriter(bout,"UTF-8"));
//response.getWriter().write("aaa"); responseProxy
chain.doFilter(request, (ServletResponse) Proxy.newProxyInstance(GzipFilter.class.getClassLoader(), response.getClass().getInterfaces(), new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(method.getName().equals("getWriter")){
return pw;
}else if(method.getName().equals("getOutputStream")){ //servletOutputStream
return new MyServletOutputStream(bout);
}else{
return method.invoke(response, args);
}
}
}));
pw.close();
byte result[] = bout.toByteArray(); //拿到目标资源的输出
System.out.println("原始大小:" + result.length);
ByteArrayOutputStream bout2 = new ByteArrayOutputStream();
GZIPOutputStream gout = new GZIPOutputStream(bout2);
gout.write(result);
gout.close();
byte gzip[] = bout2.toByteArray(); //拿到目标资源输出的压缩数据
System.out.println("压缩大小:" + gzip.length);
response.setHeader("content-encoding", "gzip");
response.setContentLength(gzip.length);
response.getOutputStream().write(gzip);
}
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
}
class MyServletOutputStream extends ServletOutputStream{
private ByteArrayOutputStream bout = null;
public MyServletOutputStream(ByteArrayOutputStream bout){
this.bout = bout;
}
@Override
public void write(int b) throws IOException {
bout.write(b);
}
}
2.缓存数据到内存
对于页面中很少更新的数据,例如商品分类,为避免每次都要从数据库查询分类数据,因此可把分类数据缓存在内存或文件中,以此来减轻数据库压力,提高系统响应速度。
//缓存数据到内存
public class CachedFilter implements Filter {
private Map<String,byte[]> map = new HashMap<String,byte[]>();
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//1.得到用户请求的uri
String uri = request.getRequestURI();
//2.看缓存中有没有uri对应的数据
byte b[] = map.get(uri);
//3.如果缓存中有,直接拿缓存的数据打给浏览器,程序返回
if(b!=null){
response.getOutputStream().write(b);
return;
}
//4.如果缓存没有,让目标资源执行,并捕获目标资源的输出
BufferResponse1 myresponse = new BufferResponse1(response);
chain.doFilter(request, myresponse);
byte out[] = myresponse.getBuffer();
//5.把资源的数据以用户请求的uri为关键字保存到缓存中
map.put(uri, out);
//6.把数据打给浏览器
response.getOutputStream().write(out);
}
public void init(FilterConfig filterConfig) throws ServletException {
}
public void destroy() {
}
}
class BufferResponse1 extends HttpServletResponseWrapper{
private ByteArrayOutputStream bout = new ByteArrayOutputStream(); //捕获输出的缓存
private PrintWriter pw;
private HttpServletResponse response;
public BufferResponse1(HttpServletResponse response) {
super(response);
this.response = response;
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return new MyServletOutputStream1(bout);
}
@Override
public PrintWriter getWriter() throws IOException {
pw = new PrintWriter(new OutputStreamWriter(bout,this.response.getCharacterEncoding()));
return pw;
}
public byte[] getBuffer(){
try{
if(pw!=null){
pw.close();
}
return bout.toByteArray();
}catch (Exception e) {
throw new RuntimeException(e);
}
}
}
class MyServletOutputStream1 extends ServletOutputStream{
private ByteArrayOutputStream bout;
public MyServletOutputStream1(ByteArrayOutputStream bout){ //接收数据写到哪里
this.bout = bout;
}
@Override
public void write(int b) throws IOException {
bout.write(b);
}
}