首页 > 技术文章 > Response

tech-bird 2014-11-23 16:00 原文

Response对象代表响应

 

响应由三部分组成:

  1. 响应行(状态码)
  2. 响应头
  3. 响应数据

 

用处:

  • 向客户端浏览器输出中文
  • 下载一个图片
  • 向页面中加入动态图片(验证码)
  • 控制浏览器定时刷新(使用refresh)
  • 控制浏览器缓存(使用expires)
  • 使用重定向

 

相关方法:

      getStatus

      setHeader

      getWrite和getOutputStream

 

其中写数据可以分为两种方式

  • 字节流  可以写任何格式的数据 
  • 字符流  只能写字符数据

 

1 如何向客户端浏览器输出中文

输出中文就涉及到编码和乱码形成的问题

不是把汉字写到客户端,而是先写到response里面

 

计算机或者网络之间的通信是没有汉字通信的,都是数字(二进制)通信

所以要把汉字通过一定的方式转化为数字

首先是把汉字通过编码(比如java内部的unicode编码翻译成十六进制等,然后再通过TCP/ip协议向下封装,最后转化为二进制)

 

1. 字节流

 

使用字节流,必须先把字符串转化为字节

public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String dataString = "中国";
        OutputStream outputStream  = response.getOutputStream();
        //要以字节流输出,所以要先把汉字转化为字节
        outputStream.write(dataString.getBytes());
    }

 

这种方式在浏览器上是没有问题的

 

但是在实际开发中,为了全球通用,一般是使用UTF-8

但是如果指定传输字节格式

public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String dataString = "中国";
        OutputStream outputStream  = response.getOutputStream();
        //指定汉字传输格式
        outputStream.write(dataString.getBytes("UTF-8"));
    }

把汉字变成字节的过程就是把从编码表上查出汉字对应的数字,然后把数字放在一个数组里面给客户机

这里查的是UTF-8编码表

那么在浏览器上就会出现乱码(即是其他的字)

原因:

  • 当把汉字定义成UTF-8时,传输的并不是汉字,而是其中对应的编码,即是数字。
  • 当把数字传给浏览器时,浏览器要进行解码,但是浏览器默认的解码方式是国标gb2312

所以会解析成其他的字

servlet写给浏览器的不是汉字中国,而是它所对应的编码

解决:

  • 在浏览器设置里面改动编码方式
  • 在服务器代码里设定浏览器的解码方式
public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //加入响应头
        response.setHeader("Content-type", "text/html;charset=UTF-8");

        String dataString = "中国";
        OutputStream outputStream  = response.getOutputStream();
        
        outputStream.write(dataString.getBytes("UTF-8"));
    }

这样的话就没有问题了。

 

 

 

2. 字符流

因为汉字本身是字符串,所以用字符流会更方便一些

 直接向response内写入一个字符流就行了,同时要注意编码

response在进行字符编码的时候默认使用的是java默认的unicode编码

public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        //设置response使用的码表,以控制respose以什么码表向浏览器写出数据
        response.setCharacterEncoding("UTF-8");
        //指定浏览器以什么码表打开服务器发送来的数据
        response.setHeader("content-type","text/html;charset=UTF-8");
        
        //以上两句的设置可以用这一句代替
        //response.setContentType("text/html;charset=UTF-8");
        
        String data = "中国";
        PrintWriter out = response.getWriter();
        out.write(data);        
    }

 

 

2 如何使用response下载一个图片

public class ResponseDemo extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {    

        //1.读这个文件(这个文件其实就是web里面的资源,在servlet里读的话用servletcontext,在普通类中使用类加载器)
        //2.读资源的话有两种方式,一种是把资源作为资源流输出, 一种是使用资源的绝对地址(这种方法可以得到文件的一些信息,比如文件名)
        String  path = this.getServletContext().getRealPath("/DownLoad/1.jpg");
        String filename = path.substring(path.lastIndexOf("\\")+1);
        
        //通知浏览器怎么打开
        //如果下载文件是中文名称,需要使用url编码
        response.setHeader("content-disposition", "attachment;filename="+URLEncoder.encode(filename,"UTF-8"));
        InputStream in= null;
        OutputStream out= null;
        try{
            in = new FileInputStream(path);
            int len = 0;
            byte buffer[] = new byte[1024];
            out = response.getOutputStream();
            while((len = in.read(buffer))>0){
                out.write(buffer,0,len);
            }
        }finally{
            if(in !=null){
                try{
                   in.close();
                }catch(Exception e){
                   e.printStackTrace();
                }
            }
            if(out !=null){
                try{
                   out.close();
                }catch(Exception e){
                   e.printStackTrace();
                }
            }
        }
        
        
    }

 

 

3. 如何向页面中加入动态图片(验证码)

 

//构建一副图片(含有汉字的验证码图片)
public class ResponseDemo2 extends HttpServlet {
    
//定义图片的长和宽
public static final int WIDTH = 200; public static final int HEIGTH = 50; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.首先在内存中构造一个图片 bufferimage 内存中的一张图片 //2.构造图象 //3.把内存中的图像输出到浏览器 //在内存中构建图像 BufferedImage image = new BufferedImage(WIDTH,HEIGTH ,BufferedImage.TYPE_INT_BGR); //向图像中写数据,拿到这个图像 Graphics g = image.getGraphics(); //1.设置背景色 setBackGround(g); //2.设置边框 setBorder(g); //3.画干扰线 drawRandomLine(g); //4.写随机数 //Graphics2D 可以旋转字符 drawRandomNumber((Graphics2D)g); //把图像写给浏览器,通过severlet的输出流 //先通知浏览器以什么格式打开,否则默认以文本打开 response.setContentType("image/jpeg"); //同时控制浏览器不能存放图片的缓存,不然刷新浏览器不能更新图片 response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache");


//向浏览器中输出图片 ImageIO.write(image,
"jpg", response.getOutputStream()); } private void drawRandomNumber(Graphics2D g) { g.setColor(Color.RED); g.setFont(new Font("宋体",Font.BOLD, 20)); //汉字区间 ,java使用的是unicode码表,汉字在内存中存的不是汉字,都是它所对应的码值 //所有汉字都在unicode码表的4e00到9fa5
//用正则表达式测试一个字符是不是汉字 [^\u4e00-\u9fa5] //常用汉字 String base = "\u7684\u4e00\u662f\u4e86\u6211\u4e0d\u4eba\u5728\u4ed6\u6709\u8fd9\u4e2a\u4e0a\u4eec\u6765\u5230\u65f6\u5927\u5730\u4e3a\u5b50\u4e2d\u4f60\u8bf4\u751f\u56fd\u5e74\u7740\u5c31\u90a3\u548c\u8981\u5979\u51fa\u4e5f\u5f97\u91cc\u540e\u81ea\u4ee5\u4f1a\u5bb6\u53ef\u4e0b\u800c\u8fc7\u5929\u53bb\u80fd\u5bf9\u5c0f\u591a\u7136\u4e8e\u5fc3\u5b66\u4e48\u4e4b\u90fd\u597d\u770b\u8d77\u53d1\u5f53\u6ca1\u6210\u53ea\u5982\u4e8b\u628a\u8fd8\u7528\u7b2c\u6837\u9053\u60f3\u4f5c\u79cd\u5f00\u7f8e\u603b\u4ece\u65e0\u60c5\u5df1\u9762\u6700\u5973\u4f46\u73b0\u524d\u4e9b\u6240\u540c\u65e5\u624b\u53c8\u884c\u610f\u52a8\u65b9\u671f\u5b83\u5934\u7ecf\u957f\u513f\u56de\u4f4d\u5206\u7231\u8001\u56e0\u5f88\u7ed9\u540d\u6cd5\u95f4\u65af\u77e5\u4e16\u4ec0\u4e24\u6b21\u4f7f\u8eab\u8005\u88ab\u9ad8\u5df2\u4eb2\u5176\u8fdb\u6b64\u8bdd\u5e38\u4e0e\u6d3b\u6b63\u611f\u89c1\u660e\u95ee\u529b\u7406\u5c14\u70b9\u6587\u51e0\u5b9a\u672c\u516c\u7279\u505a\u5916\u5b69\u76f8\u897f\u679c\u8d70\u5c06\u6708\u5341\u5b9e\u5411\u58f0\u8f66\u5168\u4fe1\u91cd\u4e09\u673a\u5de5\u7269\u6c14\u6bcf\u5e76\u522b\u771f\u6253\u592a\u65b0\u6bd4\u624d\u4fbf\u592b\u518d\u4e66\u90e8\u6c34\u50cf\u773c\u7b49\u4f53\u5374\u52a0\u7535\u4e3b\u754c\u95e8\u5229\u6d77\u53d7\u542c\u8868\u5fb7\u5c11\u514b\u4ee3\u5458\u6d4e\u8499\u68cb\u7aef\u817f\u62db\u91ca\u4ecb\u70e7\u8bef"; int x = 10; for (int i = 0; i < 5; i++) { //得到-30到30的随机数 int degree = new Random().nextInt()%30; String ch = base.charAt(new Random().nextInt(base.length()))+""; //设置旋转幅度 g.rotate(degree*Math.PI/180,x,20); g.drawString(ch, x, 20); g.rotate(-degree*Math.PI/180,x,20); x+=30; } } private void drawRandomLine(Graphics g) { g.setColor(Color.GREEN); for(int i=0;i<8;i++){ int x1= new Random().nextInt(WIDTH); int y1 = new Random().nextInt(HEIGTH); int x2= new Random().nextInt(WIDTH); int y2 = new Random().nextInt(HEIGTH); g.drawLine(x1, y1, x2, y2); } } private void setBorder(Graphics g) { //把图形上下文设置成白色 g.setColor(Color.BLUE); //用白色填充这个矩形 g.drawRect(1,1,WIDTH-2, HEIGTH-2); } private void setBackGround(Graphics g) { //把图形上下文设置成白色 g.setColor(Color.WHITE); //用白色填充这个矩形 g.fillRect(0,0,WIDTH, HEIGTH); } }

 

然后把上面的图片加到一个html页面中:

 

<!DOCTYPE html>
<html>
  <head>
    <title>register.html</title>
  </head>
  
  <body>
    <form action="">
       
        用户名:<input type="text"><br/>
        认证码:<input type="text"><img src="/day1121/servlet/ResponseDemo2" onclick="changeImage(this)" alt="换一张"  style="cursor:hand">
        
    </form>
  </body>
  
  <script type="text/javascript">
     function changeImage(img){
       img.src = img.src +"?" + new Date().getTime();
/*     在src后面加上一个随机数的目的是使得两次的url不同,这样就不会使用缓存里的图片 */
     }
  </script> 
  
</html>

 

 

4.控制浏览器定时刷新(使用refresh)

1. 本页面重新请求服务器

//控制浏览器定时刷新
public class ResponseDemo4 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        ////每隔3秒重新请求一次服务器
        response.setHeader("refresh", "3");
        String data = new Random().nextInt(100000)+"";        
        response.getWriter().write(data);
    }
}

 

2.登陆成功后,跳转到其他页面

public class ResponseDemo4 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        response.setContentType("text/html;charset=UTF-8");
        response.setHeader("refresh","3;url='/day1121/index.jsp");
        response.getWriter().write("恭喜你,登陆成功。本浏览器将在3秒后跳转到首页,如果没有跳转,请点击<a href='www.baidu.com'>超链接</a>");
    }
}

 

 

5.控制浏览器缓存(使用expires)

 

public class ResponseDemo5 extends HttpServlet {

    //控制浏览器缓存
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        //缓存一小时
        //这样在一个小时之内请求此servlet都不会向浏览器发送请求,而是直接找系统中的页面
        //IE浏览器的缓存可以直接在文件夹中查看
        response.setDateHeader("expires",System.currentTimeMillis()+ 1000*3600);        
        String dataString = "ada";
        response.getWriter().write(dataString);
    }
}

 

6.使用重定向

重定向的特点

  • 1.客户端会向服务器发送两次请求,以为着会有两个response和request
  • 2.重定向技术地址栏会发生变化

 

public class ResponseDemo6 extends HttpServlet {
    
    //实现请求重定向
    //请求重定向能不用就不用吧,因为客户端会发送两次请求,加重服务器压力
    //一定要使用请求重定向的地方:
  //1.登陆
  //因为请求重定向会使得地址栏地址发生变化,当登陆成功后跳转到首页要在地址栏显示
    //2.购物
   //当servlet购买成功后,会跳转到购物车显示页面
    //如果使用转发,购买成功后如果每次刷新,会重复买
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        //方法1. 根据http协议
        response.setStatus(302);
        response.setHeader("location", "/day1121/index.jsp");
        
        //方法2.根据sun公司的封装方法
     //相当于上面的两句
        response.sendRedirect("/day1121/index.jsp");
    }
}

 

推荐阅读