• 关于http响应内容压缩的一点小积累。


    1、在tomcat的server.xml配置文件中,添加上背景颜色为绿色的配置,服务器就会自动压缩

      <Connector port="80" maxHttpHeaderSize="8192"
                    maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
                    enableLookups="false" redirectPort="8443" acceptCount="100"
                    connectionTimeout="20000" disableUploadTimeout="true" URIEncoding="utf-8"
                    compression="on" 
                    compressionMinSize="2048" 
                    noCompressionUserAgents="gozilla, traviata" 
                    compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain"  />

    详细讲解:

    1) compression="on" 打开压缩功能
    2) compressionMinSize="2048" 启用压缩的输出内容大小,这里面默认设置为2KB
    3) noCompressionUserAgents="gozilla, traviata" 对于以下的浏览器,不启用压缩 
    4) compressableMimeType="text/html,text/xml" 压缩类型(默认为text/html,text/xml,text/plain)

    2、如果希望支持bzip2压缩,我的环境是apache cfx框架,自定义了一个跟GZIPOutInterceptor相似的拦截器,然后配置在接口的发布配置文件里。

       详细介绍如下:

    BZIPOutInterceptor代码如下:

    import java.io.IOException;
    import java.io.OutputStream;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    import java.util.ResourceBundle;
    import java.util.TreeMap;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
     
    
    import org.apache.cxf.common.i18n.BundleUtils;
    import org.apache.cxf.common.logging.LogUtils;
    import org.apache.cxf.helpers.CastUtils;
    import org.apache.cxf.helpers.HttpHeaderHelper;
    import org.apache.cxf.interceptor.Fault;
    import org.apache.cxf.interceptor.MessageSenderInterceptor;
    import org.apache.cxf.io.AbstractThresholdOutputStream;
    import org.apache.cxf.message.Exchange;
    import org.apache.cxf.message.Message;
    import org.apache.cxf.phase.AbstractPhaseInterceptor;
    import org.apache.cxf.phase.Phase;
    import org.apache.tools.bzip2.CBZip2OutputStream;
     
    /**
     * CXF interceptor that compresses outgoing messages using bzip and sets the
     * HTTP Content-Encoding header appropriately. An instance of this class should
     * be added as an out interceptor on clients that need to talk to a service that
     * accepts gzip-encoded requests or on a service that wants to be able to return
     * compressed responses. In server mode, the interceptor only compresses
     * responses if the client indicated (via an Accept-Encoding header on the
     * request) that it can understand them. To handle gzip-encoded input messages,
     * see {@link GZIPInInterceptor}. This interceptor supports a compression
     * {@link #threshold} (default 1kB) - messages smaller than this threshold will
     * not be compressed. To force compression of all messages, set the threshold to
     * 0. This class was originally based on one of the CXF samples
     * (configuration_interceptor).
     */
    public class BZIPOutInterceptor extends AbstractPhaseInterceptor<Message> {
     
        /**
         * Enum giving the possible values for whether we should gzip a particular
         * message.
         */
        public static enum UseBzip {
            NO, YES, FORCE
        }
         
        /**
         * regular expression that matches any encoding with a
         * q-value of 0 (or 0.0, 0.00, etc.).
         */
        public static final Pattern ZERO_Q = Pattern.compile(";\s*q=0(?:\.0+)?$");
         
        /**
         * regular expression which can split encodings
         */
        public static final Pattern ENCODINGS = Pattern.compile("[,\s]*,\s*");
     
        /**
         * Key under which we store the original output stream on the message, for
         * use by the ending interceptor.
         */
        public static final String ORIGINAL_OUTPUT_STREAM_KEY = BZIPOutInterceptor.class.getName()
                                                                + ".originalOutputStream";
     
        /**
         * Key under which we store an indication of whether compression is
         * permitted or required, for use by the ending interceptor.
         */
        public static final String USE_BZIP_KEY = BZIPOutInterceptor.class.getName() + ".useBzip";
     
        /**
         * Key under which we store the name which should be used for the
         * content-encoding of the outgoing message. Typically "gzip" but may be
         * "x-gzip" if we are processing a response message and this is the name
         * given by the client in Accept-Encoding.
         */
        public static final String BZIP_ENCODING_KEY = BZIPOutInterceptor.class.getName() + ".bzipEncoding";
         
        public static final String SOAP_JMS_CONTENTENCODING = "SOAPJMS_contentEncoding";
     
        private static final ResourceBundle BUNDLE = BundleUtils.getBundle(BZIPOutInterceptor.class);
        private static final Logger LOG = LogUtils.getL7dLogger(BZIPOutInterceptor.class);
     
     
        /**
         * Compression threshold in bytes - messages smaller than this will not be
         * compressed.
         */
        private int threshold = 1024;
        private boolean force;
     
        public BZIPOutInterceptor() {
            super(Phase.PREPARE_SEND);
            addAfter(MessageSenderInterceptor.class.getName());
        }
        public BZIPOutInterceptor(int threshold) {
            super(Phase.PREPARE_SEND);
            addAfter(MessageSenderInterceptor.class.getName());
            this.threshold = threshold;
        }
     
        public void setThreshold(int threshold) {
            this.threshold = threshold;
        }
     
        public int getThreshold() {
            return threshold;
        }
     
        public void handleMessage(Message message) throws Fault {
            UseBzip use = bzipPermitted(message);
            
            if (use != UseBzip.NO) {
                // remember the original output stream, we will write compressed
                // data to this later
                OutputStream os = message.getContent(OutputStream.class);
                
                if (os == null) {
                    return;
                }
                
                message.put(ORIGINAL_OUTPUT_STREAM_KEY, os);
                message.put(USE_BZIP_KEY, use);
                
                // new stream to cache the message
                
                BZipThresholdOutputStream cs 
                    = new BZipThresholdOutputStream(threshold,
                                                    os,
                                                    use == UseBzip.FORCE,
                                                    message);
                message.setContent(OutputStream.class, cs);
            }
        }
        
        /**
         * Checks whether we can, cannot or must use gzip compression on this output
         * message. Gzip is always permitted if the message is a client request. If
         * the message is a server response we check the Accept-Encoding header of
         * the corresponding request message - with no Accept-Encoding we assume
         * that gzip is not permitted. For the full gory details, see <a
         * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3">section
         * 14.3 of RFC 2616</a> (HTTP 1.1).
         * 
         * @param message the outgoing message.
         * @return whether to attempt gzip compression for this message.
         * @throws Fault if the Accept-Encoding header does not allow any encoding
         *                 that we can support (identity, gzip or x-gzip).
         */
        private UseBzip bzipPermitted(Message message) throws Fault {
            UseBzip permitted = UseBzip.NO;
            if (isRequestor(message)) {
                LOG.fine("Requestor role, so bzip enabled");
                Object o = message.getContextualProperty(USE_BZIP_KEY);
                if (o instanceof UseBzip) {
                    permitted = (UseBzip)o;
                } else if (o instanceof String) {
                    permitted = UseBzip.valueOf((String)o);
                } else {
                    permitted = force ? UseBzip.YES : UseBzip.NO;
                }
                message.put(BZIP_ENCODING_KEY, "bzip");
                addHeader(message, "Accept-Encoding", "bzip;q=1.0, identity; q=0.5, *;q=0"); 
            } else {
                LOG.fine("Response role, checking accept-encoding");
                Exchange exchange = message.getExchange();
                Message request = exchange.getInMessage();
                Map<String, List<String>> requestHeaders = CastUtils.cast((Map<?, ?>)request
                    .get(Message.PROTOCOL_HEADERS));
                if (requestHeaders != null) {
                    List<String> acceptEncodingHeader = CastUtils.cast(HttpHeaderHelper
                        .getHeader(requestHeaders, HttpHeaderHelper.ACCEPT_ENCODING));
                    List<String> jmsEncodingHeader = CastUtils.cast(requestHeaders.get(SOAP_JMS_CONTENTENCODING));
                    if (jmsEncodingHeader != null && jmsEncodingHeader.contains("bzip")) {
                        permitted = UseBzip.YES;
                        message.put(BZIP_ENCODING_KEY, "bzip");
                    }
                    if (acceptEncodingHeader != null) {
                        if (LOG.isLoggable(Level.FINE)) {
                            LOG.fine("Accept-Encoding header: " + acceptEncodingHeader);
                        }
                        // Accept-Encoding is a comma separated list of entries, so
                        // we split it into its component parts and build two
                        // lists, one with all the "q=0" encodings and the other
                        // with the rest (no q, or q=<non-zero>).
                        List<String> zeros = new ArrayList<String>(3);
                        List<String> nonZeros = new ArrayList<String>(3);
     
                        for (String headerLine : acceptEncodingHeader) {
                            String[] encodings = ENCODINGS.split(headerLine.trim());
     
                            for (String enc : encodings) {
                                Matcher m = ZERO_Q.matcher(enc);
                                if (m.find()) {
                                    zeros.add(enc.substring(0, m.start()));
                                } else if (enc.indexOf(';') >= 0) {
                                    nonZeros.add(enc.substring(0, enc.indexOf(';')));
                                } else {
                                    nonZeros.add(enc);
                                }
                            }
                        }
     
                        // identity encoding is permitted if (a) it is not
                        // specifically disabled by an identity;q=0 and (b) if
                        // there is a *;q=0 then there is also an explicit
                        // identity[;q=<non-zero>]
                        //
                        // [x-]gzip is permitted if (a) there is an explicit
                        // [x-]gzip[;q=<non-zero>], or (b) there is a
                        // *[;q=<non-zero>] and no [x-]gzip;q=0 to disable it.
                        boolean identityEnabled = !zeros.contains("identity")
                                                  && (!zeros.contains("*") || nonZeros.contains("identity"));
                        boolean bzipEnabled = nonZeros.contains("bzip")
                                              || (nonZeros.contains("*") && !zeros.contains("bzip"));
     
                        if (identityEnabled && !bzipEnabled) {
                            permitted = UseBzip.NO;
                        } else if (identityEnabled && bzipEnabled) {
                            permitted = UseBzip.YES;
                            message.put(BZIP_ENCODING_KEY, "bzip");
                        } else if (!identityEnabled && bzipEnabled) {
                            permitted = UseBzip.FORCE;
                            message.put(BZIP_ENCODING_KEY, "bzip");
                        } else {
                            throw new Fault(new org.apache.cxf.common.i18n.Message("NO_SUPPORTED_ENCODING",
                                                                                   BUNDLE));
                        }
                    } else {
                        LOG.fine("No accept-encoding header");
                    }
                }
            }
     
            if (LOG.isLoggable(Level.FINE)) {
                LOG.fine("bzip permitted: " + permitted);
            }
            return permitted;
        }
         
        static class BZipThresholdOutputStream extends AbstractThresholdOutputStream {
            Message message;
             
            public BZipThresholdOutputStream(int t, OutputStream orig,
                                             boolean force, Message msg) {
                super(t);
                super.wrappedStream = orig;
                message = msg;
                if (force) {
                    setupBZip();
                }
            }
             
            private void setupBZip() {
                 
            }
     
            @Override
            public void thresholdNotReached() {
                //nothing
                LOG.fine("Message is smaller than compression threshold, not compressing.");
            }
     
            @Override
            public void thresholdReached() throws IOException {
                LOG.fine("Compressing message.");
                // Set the Content-Encoding HTTP header
                String enc = (String)message.get(BZIP_ENCODING_KEY);
                addHeader(message, "Content-Encoding", enc);
                // if this is a response message, add the Vary header
                if (!Boolean.TRUE.equals(message.get(Message.REQUESTOR_ROLE))) {
                    addHeader(message, "Vary", "Accept-Encoding");
                } 
                // bzip the result
                CBZip2OutputStream bZip2Output = new CBZip2OutputStream(wrappedStream);
                wrappedStream = bZip2Output;
            }
        }
         
        /**
         * Adds a value to a header. If the given header name is not currently
         * set in the message, an entry is created with the given single value.
         * If the header is already set, the value is appended to the first
         * element of the list, following a comma.
         * 
         * @param message the message
         * @param name the header to set
         * @param value the value to add
         */
        private static void addHeader(Message message, String name, String value) {
            Map<String, List<String>> protocolHeaders = CastUtils.cast((Map<?, ?>)message
                .get(Message.PROTOCOL_HEADERS));
            if (protocolHeaders == null) {
                protocolHeaders = new TreeMap<String, List<String>>(String.CASE_INSENSITIVE_ORDER);
                message.put(Message.PROTOCOL_HEADERS, protocolHeaders);
            }
            List<String> header = CastUtils.cast((List<?>)protocolHeaders.get(name));
            if (header == null) {
                header = new ArrayList<String>();
                protocolHeaders.put(name, header);
            }
            if (header.size() == 0) {
                header.add(value);
            } else {
                header.set(0, header.get(0) + "," + value);
            }
        }
        public void setForce(boolean force) {
            this.force = force;
        }    
     
    }

    配置文件:

        <jaxrs:server id="" address="/">
          <jaxrs:outInterceptors>
               <bean class="BZIPOutInterceptor"></bean>
           </jaxrs:outInterceptors>
            <jaxrs:serviceBeans>
                <ref bean="" />
            </jaxrs:serviceBeans>
            <jaxrs:extensionMappings>
                <entry key="xml" value="application/xml" />
            </jaxrs:extensionMappings>
            <jaxrs:languageMappings>
                <entry key="en" value="en-gb" />
            </jaxrs:languageMappings>
        </jaxrs:server>

    这样便能够实现如果请求头里Accept-Encoding 包含bzip压缩,服务器就自动进行bzip2压缩了。

  • 相关阅读:
    搭建Java 开发环境
    自己制作 Android Vector Asset 矢量图
    Manifest merger failed with multiple errors, see logs 问题处理
    Android 获取所在城市(不接入地图SDK,使用android自带的SDK定位)
    根据视频地址获取封面
    Git 的一些使用细枝末节
    关于onActivityResult方法不执行的问题汇总
    关于handler 和 looper 的问题
    关于action和category的认知区别
    关于activity的生命周期的随笔
  • 原文地址:https://www.cnblogs.com/ld-swust/p/5199797.html
Copyright © 2020-2023  润新知