Tag:spring
Article From:https://www.cnblogs.com/yucongblog/p/9122409.html

 

Non original article: original link: http://blog.csdn.net/qq_33206732/article/details/78623042

A few days ago, our project director gave me a task to compress the requested interface data in order to achieve the purpose of saving traffic.

In order to realize this function, there are the following ideas:

1.Get the value in response,
2.Gzip compression for data (because the front end is not changed, so it can only be selected in the compression mode supported by this browser).
3.Write the data into the response
4.Response return to the front end

However, when I carried out the first step, I encountered a very painful thing. The return data in response could not be taken. There was no language, and no processing method was allowed in each interface method. The first thought was the afterCompletion () side in the interceptor.Data processing in France, but response does not provide a way to get body value.

Through online search, there is a way to get data in response, which is implemented by HttpServletResponseWrapper wrapping HttpServletResponse.

There are about two versions of the data in response through HttpServletResponseWrapper, and there is a large number of versions, but it doesn’t work at all, that’s the following code:

public class ResponseWrapper extends HttpServletResponseWrapper {
    private PrintWriter cachedWriter;
    private CharArrayWriter bufferedWriter;

    public ResponseWrapper(HttpServletResponse response) throws IOException {
        super(response);
        bufferedWriter = new CharArrayWriter();
        cachedWriter = new PrintWriter(bufferedWriter);
    }

    public PrintWriter getWriter() throws IOException {
        return cachedWriter;
    }

    public String getResult() {
        byte[] bytes = bufferedWriter.toString().getBytes();
        try {
            return new String(bytes, "UTF-8");
        } catch (Exception e) {
            LoggerUtil.logError(this.getClass().getName(), "getResult", e);
            return "";
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

After the test getResult () can not get the value, the specific people can study the above code, you know why, it is completely a pit ah, here is not much said.

There is another version, which I use now. (first, thank you for this friend, the specific original path will be pasted below), the following is my code.
I have a problem with the original code, I do not know whether it is all this problem, or if I have any problems, what problems will be addressed below and how to solve them?

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.*;

public class ResponseWrapper extends HttpServletResponseWrapper {

    private ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    private HttpServletResponse response;
    private PrintWriter pwrite;

    public ResponseWrapper(HttpServletResponse response) {
        super(response);
        this.response = response;
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return new MyServletOutputStream(bytes); // Write the data into byte}/**
     * Overwrite the getWriter () method of the parent class and cache the response data in PrintWriter.* * /@Override
    public PrintWriter getWriter() throws IOException {
        try{
            pwrite = new PrintWriter(new OutputStreamWriter(bytes, "utf-8"));
        } catch(UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return pwrite;
    }

    /**
     * Get the response data from the cache in PrintWriter* @return
     */
    public byte[] getBytes() {
        if(null != pwrite) {
            pwrite.close();
            return bytes.toByteArray();
        }

        if(null != bytes) {
            try {
                bytes.flush();
            } catch(IOException e) {
                e.printStackTrace();
            }
        }
        return bytes.toByteArray();
    }

    class MyServletOutputStream extends ServletOutputStream {
        private ByteArrayOutputStream ostream ;

        public MyServletOutputStream(ByteArrayOutputStream ostream) {
            this.ostream = ostream;
        }

        @Override
        public void write(int b) throws IOException {
            ostream.write(b); // Write the data into stream}}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

Because the HttpServletResponse wrapper class can only be used in the filter, it can only be implemented in the filter, and the following is the code of the doFilter () method of my filter:

 @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        String headEncoding = ((HttpServletRequest)servletRequest).getHeader("accept-encoding");
        if (headEncoding == null || (headEncoding.indexOf("gzip") == -1)) { // The client does not support gzipFilterChain.doFilter (servletRequest, servletResponse);System.out.println("----------------The browser does not support gzip format encoding - "-".}else { // Support gzip compression and gzip compression of dataHttpServletRequest req = (HttpServletRequest) servletRequest;HttpServletResponse resp = (HttpServletResponse) servletResponse;ResponseWrapPer mResp =new ResponseWrapper(resp); // Wrapper the response object resp and cache the response dataFilterChain.doFilter (req, mResp);byte[] bytes = mResp.getBytes(); // Getting the response data of the cacheSystem.out.println("Pre compression size: "+ bytes.length";System.out.println("Pre compression data: "+new String(bytes,"utf-8"));

            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            GZIPOutputStream gzipOut = new GZIPOutputStream(bout); // Create GZIPOutputStream objectsGzipOut.write (bytes);// Write the response data to the Gzip compression streamGzipOut.flush ();GzipOut.close ();// Refresh the data to an array of bout bytes flowbyte[] bts = bout.toByteArray();
            System.out.println("After compression, "+ bts.length";Resp.setHeader ("Content-Encoding", "gzip"); // Setting the response header informationResp.getOutputStream ().Write (BTS);// Response the compressed data to the client}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

Here I explain the above code, first to judge whether the request request accepts gzip compression, which is based on the accept-encoding property of the request’s request header, because all the big browsers now support gzip, soIf you want to do gzip compression, the front end only needs to be added, and if the back end data is the data compressed by gzip, the browser will automatically unzip it.

The above code
If you do not support gzip compression, do not deal with the normal process down. It
If gzip compression is supported, data processing is needed

You can look at this code

filterChain.doFilter(req, mResp);
  • 1

This method is important. The front part of this method is part of the prior request interface, and if you have something that you want to process before calling the interface, you can process it before, of course, you can also handle it in the interceptor’s preHandle () method. The following part of the corresponding method isThe request interface has a part after the return value. That is, this time we need to do some data compression.

Of course, you need to pay attention to the second parameters of doFilter, which are originally ServletResponse objects, but now because of processing data, we use the ResponseWrapper class to wrap the ServletResponse, so secondThe parameter is passed on to the ResponseWrapper object, and of course, if you wrap the servletRequest, then the first parameter is to pass you the object of the servletRequest class.

The next is to use the wrapper object to get the returned data, then use GZIPOutputStream to compress the data, and then use resp.getOutputStream ().Write (BTS); write the compressed data to reSponse, of course, we can not forget that we need to add Content-Encoding (return content encoding) to the gzip format in the request header.

In this way, we can take out the data in response and return it to the front end after compression. Of course, you do not have to compress, you can also encrypt and so on.

In the above process, I encountered a problem, need to pay attention to, do not know if you have encountered,
It is the process above is normal, the data is also obtained, compression is also compressed, the execution time is printed out, but the front is always in response, that is, we respond too slow, I see, the average in about 30 seconds, this is no way to receive.

At first I thought it was too slow to decompress the gzip data at the front end, but I shielded the gzip related code and returned the data back the same slow, so gzip compression decompression was eliminated.

Then there is only one place to have a problem, that is, our packing class ResponseWrapper has a problem, and through debug, I find the order of the execution of the various methods in the class we encapsulate.

First, when we new an object, we call its construction method, ResponseWrapper (HttpServletResponse response) method, and then call the package when executing the doFilter method of the filter.The getOutputStream () method of the class writes the data into the ByteArrayOutputStream we define, that is, bytes, and then we call the getBytes () method to convert the bytes into an array of byteReturn, and here is our return data.

As we can see from the above process, there is no problem in theory. In fact, we also get the data we want, and these methods are very fast, not in any part of the carton. Where did the problem come from? I searched the Internet for a long time. There was little information in it. Finally, I wrote it in a blog.This code is that before writing data, we need to use Response object to recharge contentLength. And that’s the following code

response.setContentLength(-1);
  • 1

I just didn’t think where to add this code here, I thought it was in the filter, but think about it, it’s not the right time to join, then look at the wrapper, and find that the guy who writes this code defines a HttpServletResponse object, and it’s also in the constructor method.It is initialized. But the full text does not use this response object. I wonder if we add this code before we execute the method by calling getOutputStream () to write data to bytes. Try it. It’s really good. At this point the problem is solved.

This time it took me a day to solve the corresponding slow problem, but it also gained a lot of things. So here, thanks for the buddies in the code above, and the buddies who wrote the short blog but solved my final problem.

The following is the address of two Blogs:
http://blog.csdn.net/yy417168602/article/details/53534776 
http://blog.csdn.net/qbian/article/details/53909778

Leave a Reply

Your email address will not be published. Required fields are marked *