Java Web 减少网络 IO、静态资源磁盘 IO 有效的办法--响应使用 GZIP( 压缩http请求与响应gzip压缩)

时间:2021-09-18 19:59:35

(转载http://blog.csdn.net/hylclxy/article/details/7779662)

 

出于节约流量考虑, 客户端在向服务端发送request的时候对post数据进行gzip压缩, 同时服务端把返回的数据也进行gzip压缩. 为防止遗忘, 记录在此.

 
  1.   

 

编写工具类GzipUtil.java, 开始没考虑好, 方法实现得较乱:

 

 

  1.     public static String METHOD_POST = "POST";  
        public static final String ACCEPT_ENCODING = "Accept-Encoding";   
        public static final String CONTENT_ENCOING = "Content-Encoding";  
        public static final String CONTENT_LENGTH = "Content-Length";  
        public static final String ENCODING_GZIP = "gzip";  
        public static final String MIME_APPLICATION_X_GZIP = "application/x-gzip";  
        public static final String ENCODING = "UTF-8";  
      
        /** 
         * 对参数进行gzip压缩操作, 返回字节数组 
         * @param data 待压缩的字节数组 
         * @return  压缩后的gzip字节数组 
         * @throws IOException  
         */  
        public static byte[] gzip(byte[] data) throws IOException{  
              
            ByteArrayOutputStream baos = new ByteArrayOutputStream();  
              
            GZIPOutputStream gos = new GZIPOutputStream(baos);  
              
            gos.write(data);  
              
            gos.finish();  
            gos.flush();  
              
            byte[] result = baos.toByteArray();  
              
            baos.flush();  
              
            try{  
                gos.close();  
            }catch(IOException e){  
                logger.warn("Close GZIPOutputStream fail:", e);  
            }  
              
            return result;  
        }  
      
        /** 
         * 对参数进行gzip压缩操作, 结果输出到参数的输出流中 
         * @param data 待压缩的字节数组 
         * @param os    压缩后的输出流 
         * @throws IOException  
         */  
        public static void gzip(byte[] data, OutputStream os) throws IOException{  
              
            GZIPOutputStream gos = new GZIPOutputStream(os);  
              
            gos.write(data);  
              
            gos.finish();  
            gos.flush();  
              
            try{  
                gos.close();  
            }catch(IOException e){  
                logger.warn("Close GZIPOutputStream fail:", e);  
            }  
              
        }  
          
        /** 
         * 对输入流进行gzip压缩  
         * @param ins   待压缩的输入流 
         * @param os    压缩后的输出流 
         * @throws IOException 
         */  
        public static void gzip(InputStream ins, OutputStream os) throws IOException{  
              
            GZIPOutputStream gos = new GZIPOutputStream(os);  
              
            int b;  
            while((b = ins.read()) != -1){  
                gos.write(b);  
            }  
              
            gos.finish();  
            gos.flush();  
              
        }  
          
        /** 
         * 解压缩 
         * @param ins 输入流 
         * @return  解压完的数据字节数组 
         * @throws IOException  
         */  
        public static byte[] unGzip(InputStream ins) throws IOException{  
              
            if(logger.isInfoEnabled()){  
                logger.info("Start to ungzip parameters.....");  
            }  
              
            GZIPInputStream gis = new GZIPInputStream(ins);  
            ByteArrayOutputStream baos = new ByteArrayOutputStream();  
              
            int b;  
            while((b = gis.read()) != -1){  
                baos.write(b);  
            }  
              
            byte[] result = baos.toByteArray();  
              
            if(logger.isInfoEnabled()){  
                logger.info("Ungzip parameters bytes OK, result bytes size is: " + result.length);  
            }  
              
            try{  
                gis.close();  
            }catch(IOException e){  
                logger.warn("Close GZIPInputStream fail:", e);  
            }  
              
            return result;  
        }  
          
        /** 
         * 对输入流的数据解压缩 
         * @param ins   待解压的输入流 
         * @param os    解压后的输出流 
         * @throws IOException 
         */  
        public static void unGzip(InputStream ins, OutputStream os) throws IOException{  
              
            GZIPInputStream gis = new GZIPInputStream(ins);  
              
            int b;  
            while((b = gis.read()) != -1){  
                os.write(b);  
            }  
              
            try{  
                gis.close();  
            }catch(IOException e){  
                logger.warn("Close GZIPInputStream fail:", e);  
            }  
        }  
          
        /** 
         * 把从gzip流中解压出来的参数和request原有的参数合并成一个新Map返回 
         *  
         * 客户端传来的数据没有经过url编码, 不需要解码 
         * @param data  解压后的字节数组 
         * @param res   请求的request  
         * @return 完整的parameterMap 
         * @throws UnsupportedEncodingException  
         */  
        public static Map<String, Object> getParameterMap(byte[] data, ServletRequest request) throws UnsupportedEncodingException{  
            String body = null;  
              
            if(data != null && data.length > 0){  
                body = new String(data, ENCODING);  
            }  
              
            if(body != null && !"".equals(body.trim())){  
                  
                if(logger.isInfoEnabled()){  
                    logger.info("Ungzip parameters string is : " + body);  
                }  
                  
                //新parameterMap  
                Map<String, Object> paramsMap = new LinkedHashMap<String, Object>();  
                //把原有parameterMap添加到新map中  
                if(request.getParameterMap() != null){  
                    paramsMap.putAll(request.getParameterMap());  
                }  
                 
                String splitMapFlag = "&";  
                String spiltKeyValueFlag = "=";  
                  
                String[] mapArr = body.split(splitMapFlag);  
                String[] arr = null;  
                for(String tagValue : mapArr){  
                    arr = tagValue.split(spiltKeyValueFlag, 2);  
                    if(arr.length == 2){  
                        if(paramsMap.containsKey(arr[0]) && paramsMap.get(arr[0]) != null){  
    //                        List<String> list = new ArrayList<String>();  
    //                        list.addAll(Arrays.asList((String[])paramsMap.get(arr[0])));  
    //                        list.add(arr[1]);  
    //                        paramsMap.put(arr[0], list.toArray(new String[list.size()]));  
                              
    //                        String[] oldArr = (String[])paramsMap.get(arr[0]);  
    //                        String[] newArr = new String[oldArr.length + 1];  
    //                        System.arraycopy(oldArr, 0, newArr, 0, oldArr.length);  
    //                        newArr[newArr.length - 1] = arr[1];  
    //                        paramsMap.put(arr[0], newArr);  
                              
                            String[] array = (String[])paramsMap.get(arr[0]);;  
                            array = Arrays.copyOf(array, array.length + 1);  
                            array[array.length - 1] = arr[1];  
                            paramsMap.put(arr[0], array);  
                        }else{  
                            paramsMap.put(arr[0], new String[]{arr[1]});  
                        }  
                    }  
                }  
                return paramsMap;  
            }  
              
            return request.getParameterMap();  
        }  
          

     

 


修改filter类, 同时兼容不支持gzip压缩的老版本客户端:

 

 

  1. //判断返回给客户端的响应流是否是需要经过gzip压缩  
            String acceptEncoding = req.getHeader(GZipUtil.ACCEPT_ENCODING);  
            if (acceptEncoding != null  
                && acceptEncoding.trim().equalsIgnoreCase(GZipUtil.ENCODING_GZIP))  
            {  
                res.setHeader(GZipUtil.CONTENT_ENCOING, GZipUtil.ENCODING_GZIP);  
                GZipResponse gzipResponse = new GZipResponse(res);  
                  
                //contentType为application/x-gzip, 代表客户端压缩了请求参数, 需要解压  
                String contentType = req.getContentType();  
                if (contentType != null  
                    && contentType.equalsIgnoreCase(GZipUtil.MIME_APPLICATION_X_GZIP))  
                {  
                    byte[] data = GZipUtil.unGzip(req.getInputStream());  
                    Map<String, Object> newParameterMap = GZipUtil.getParameterMap(data,  
                        req);  
                    RequestParameterWrapper requestWrapper = new RequestParameterWrapper(  
                        req, newParameterMap);  
                    chain.doFilter(requestWrapper, gzipResponse);  
                }  
                else  
                {  
                    chain.doFilter(req, gzipResponse);  
                }  
            }  
            else  
            {  
                chain.doFilter(request, res);  
            }  
     

     

 

因为请求内容有被压缩的post参数未正常保存在request中, 建立新类RequestParameterWrapper(继承HttpServletRequestWrapper, 调用gzipUtil的方法处理)重新包装请求参数, 同时使用GZipResponse extends HttpServletResponseWrapper对响应数据进行gzip压缩.

RequestParameterWrapper:

  

public class RequestParameterWrapper extends HttpServletRequestWrapper  
{  
    private final Map params;  
    public RequestParameterWrapper(HttpServletRequest request, Map newParams)  
    {  
        super(request);  
        this.params = newParams;  
    }  
      
    @Override  
    public Map getParameterMap(){  
        return params;  
    }  
  
    @Override  
    public Enumeration getParameterNames(){  
        Vector l = new Vector(params.keySet());  
        return l.elements();  
    }  
      
    @Override  
    public String[] getParameterValues(String name){  
        Object v = params.get(name);  
        if(v == null){  
            return null;  
        }else if(v instanceof String[]){  
            return (String[])v;  
        }else if(v instanceof String){  
            return new String[]{(String)v};  
        }else{  
            return new String[]{v.toString()};  
        }  
    }  
      
    @Override  
    public String getParameter(String name){  
        Object v = params.get(name);  
        if(v == null){  
            return null;  
        }else if(v instanceof String[]){  
            return ((String[])v)[0];  
        }else if(v instanceof String){  
            return (String)v;  
        }else{  
            return v.toString();  
        }  
    }  
      
}  

 

GZipResponse:

 

 

[java]  view plain copy
 
  1. public class GZipResponse extends HttpServletResponseWrapper  
    {  
        private GZIPServletStream wrappedOut;  
          
        public GZipResponse(HttpServletResponse response) throws IOException  
        {  
            super(response);  
            wrappedOut = new GZIPServletStream(response);  
        }  
          
        public ServletOutputStream getOutputStream() throws IOException  
        {  
            return wrappedOut;  
        }  
          
        private PrintWriter wrappedWriter;  
          
        public PrintWriter getWriter() throws IOException  
        {  
            if (wrappedWriter == null)  
            {  
                wrappedWriter = new PrintWriter(new OutputStreamWriter(  
                    getOutputStream(), getCharacterEncoding()));  
            }  
            return wrappedWriter;  
        }  
          
        public void flush() throws IOException  
        {  
            if (wrappedWriter != null)  
            {  
                wrappedWriter.flush();  
            }  
        }  
          
        private class GZIPServletStream extends ServletOutputStream  
        {  
            private HttpServletResponse response;  
            private OutputStream outputStream;  
            private ByteArrayOutputStream baos = new ByteArrayOutputStream();  
              
            public GZIPServletStream(HttpServletResponse response) throws IOException  
            {  
                this.response = response;  
                outputStream = response.getOutputStream();  
            }  
              
              
            public void write(byte[] buf) throws IOException  
            {  
                baos.write(buf);  
            }  
              
            public void write(byte[] buf, int off, int len) throws IOException  
            {  
               baos.write(buf, off, len);  
            }  
              
            public void write(int c) throws IOException  
            {  
                baos.write(c);  
            }  
              
            public void write(byte buf) throws IOException  
            {  
                baos.write(buf);  
            }  
              
            public void flush() throws IOException  
            {  
    //            byte[] bytes = baos.toByteArray();  
    //            GZipUtil.gzip(bytes, outputStream);  
                  
                //需要把压缩后的字节大小设置到response的content-length中  
                byte[] bytes = GZipUtil.gzip(baos.toByteArray());  
                outputStream.write(bytes);  
                  
                outputStream.flush();  
                response.setHeader(GZipUtil.CONTENT_LENGTH, String.valueOf(bytes.length));  
            }  
              
            public void close() throws IOException  
            {  
                outputStream.close();  
            }  
        }  
    }