フィルタは、HttpServeretResponseWrapperパッケージHttpServeretResponseによりresponseからの戻りデータの取得、およびデータのgzip圧縮を実現する
12149 ワード
先日、プロジェクトディレクターからリクエストされたインタフェースデータを圧縮して、トラフィックを節約する目的を達成するタスクがありました.
この機能を実現するには、次のような考え方があります.
1.responseの値を取得します.2.データをgzip圧縮する(フロントエンドが変わらないことが要求されるため、このブラウザでサポートされている圧縮方式しか選択できない).responseにデータを書き込む.responseをフロントエンドに返品
しかし、私が最初のステップを実行すると、卵が痛いことに遭遇し、responseの戻りデータが取れなくなり、ここでは無言で、インタフェースメソッドごとに処理方法を加えることは許されず、最初はブロッカーのafterCompletion()メソッドでデータ処理を行うことを考えていましたが、responseではbody値を取得する方法は提供されていませんでした.自分で何とかするしかない.
インターネットで検索することで、responseのデータを取得する方法があります.HttpServeretResponseWrapperパッケージHttpServeretResponseを使用して実現します.
HttpServeretResponseWrapperでresponseのデータを取得するには、2つのバージョンがあり、1つのバージョンの数は多いですが、まったく役に立たないでしょう.次のコードです.
テストgetResult()では値が得られません.具体的には、上記のコードを検討してみると、なぜか、完全に穴ですね.ここではあまり言いません.
もう一つのバージョン、つまり私が今使っている(ここではまずこの兄弟たちに感謝します.具体的な元の経路はすぐ下に貼られます)です.下には私のコードの元のコードがあります.私のところに問題があります.この問題があるのか、それとも私に問題があるのか、下にはどんな問題があるのか、どうやって解決したのか分かりません.
HttpServeretResponseのパッケージクラスはフィルタでしか使用できないので、フィルタでしか実現できません.以下は私のフィルタのdoFilter()メソッドのコードです.
ここで上のコードを説明して、まずrequestリクエストがgzip圧縮を受け入れるかどうかを判断します.これはrequestのリクエストヘッダのaccept-encodingという属性に基づいて判断します.現在の各ブラウザはgzipをサポートしているので、gzip圧縮をしたい場合は、フロントエンドはこのリクエストヘッダを加えるだけで、バックエンドから返されるデータがgzip圧縮されたデータであれば、ブラウザは自動的に解凍します.
上のコードがgzip圧縮をサポートしていない場合は、処理しないで、通常の流れを下に進みます.gzip圧縮がサポートされている場合は、データ処理が必要です.
このコードを見てみましょう
この方法は重要で、この方法の前の部分はすべてインタフェースを要求する前の部分で、もしあなたがインタフェースを呼び出す前に統一的に処理したいものがあれば、もちろんブロックのpreHandle()方法で処理することもできます.対応するこの方法の後の部分は,要求インタフェースに戻り値がある後の部分である.つまり今回はデータを圧縮する部分が必要です.
もちろん注意が必要なのはdoFilterの2番目のパラメータで、もともとサーブレットResponseオブジェクトだったのですが、今はデータを処理するためにResponseWrapperクラスを使ってサーブレットResponseをパッケージしているので、2番目のパラメータはResponseWrapperオブジェクトを伝えています.もちろん、servletRequestをパッケージしている場合は、では、最初のパラメータはservletRequestクラスをパッケージしたオブジェクトを伝えます.
次に、パッケージクラスのオブジェクトで返すデータを取得し、GZIPOutputStreamを使用してデータを圧縮するrespを使用する.getOutputStream().write(bts); 圧縮されたデータをresponseに書き込むには、当然、返されるリクエストヘッダにContent-Encoding(返されるコンテンツ符号化)をgzip形式とする必要があることを忘れてはいけない.
これによりresponseのデータを取り出して圧縮してフロントエンドに戻ることができます.もちろん圧縮する必要はありません.暗号化などの処理もできます.
上の流れの中で、私は1つの问题に出会って、注意しなければならなくて、あなた达が出会ったかどうか分かりませんが、上の流れはすべて正常に行って、データも取得して、圧縮も圧縮して、実行时间も印刷して、しかしフロントエンドはずっと応答の中で、つまり私达の応答の遅さ、私は见て、平均は30秒ぐらいで、これでは納得できない.
最初はフロントエンドがgzipデータを解凍する速度が遅すぎると思っていましたが、gzip関連コードを遮断して、データの戻りが同じように遅いので、gzip圧縮解凍排除しました.
そして一つだけ問題があります.それは私たちのパッケージクラスResponseWrapperに問題があります.debugを通じて、私たちがパッケージしたクラスの各方法の実行順序を発見しました.
まず、newオブジェクトの構築方法ResponseWrapper(HttpServertResponse response)メソッドを呼び出し、フィルタのdoFilterメソッドを実行するときにパッケージクラスのgetoutputStream()メソッドを呼び出して、定義したByteArrayOutputStream、すなわちbyttesにデータを書き込みます.次にgetBytes()メソッドを呼び出してbytesをbyte配列に変換して返します.これが戻りデータです.
上記の流れから,理論的には問題なく,実際には我々が望んでいるデータも得られ,これらの方法は実行速度も速く,どの部分にもカートンが住んでいないことが分かった.では、問題はどこにあるのでしょうか.私はネットで半日探しましたが、この方面の資料は少ないです.最後にブログで、このコードを書いたのは、データを書く前にResponseオブジェクトを使ってcontentLengthをチャージする必要があります.つまり次のコードです
ここで最初はどこにこのコードを入れるか考えていませんでしたが、フィルターの中で考えていましたが、入れるタイミングが間違っていたと思いました.その後、パッケージ類を見てみると、このコードを書いた兄たちがHttpServeretResponseオブジェクトを定義し、構造方法でも初期化していることがわかりました.しかし全文はこのresponseオブジェクトには用いられなかった.getoutputStream()を呼び出してbytesにデータを書き込む前にこのコードを付けたのではないかと思います.やってみましたが、本当にいいですね.これで問題が解決する.
今回の需要は、相応の緩やかな問題をどのように解決するかに1日かかりましたが、多くのものを収穫しました.だからここでコードの皆さんに感謝します.そして、短いですが、私の最終的な問題を解決したブログを書いた皆さんに感謝します.
次は2つのブログのアドレスです.http://blog.csdn.net/yy417168602/article/details/53534776 http://blog.csdn.net/qbian/article/details/53909778
この機能を実現するには、次のような考え方があります.
1.responseの値を取得します.2.データをgzip圧縮する(フロントエンドが変わらないことが要求されるため、このブラウザでサポートされている圧縮方式しか選択できない).responseにデータを書き込む.responseをフロントエンドに返品
しかし、私が最初のステップを実行すると、卵が痛いことに遭遇し、responseの戻りデータが取れなくなり、ここでは無言で、インタフェースメソッドごとに処理方法を加えることは許されず、最初はブロッカーのafterCompletion()メソッドでデータ処理を行うことを考えていましたが、responseではbody値を取得する方法は提供されていませんでした.自分で何とかするしかない.
インターネットで検索することで、responseのデータを取得する方法があります.HttpServeretResponseWrapperパッケージHttpServeretResponseを使用して実現します.
HttpServeretResponseWrapperでresponseのデータを取得するには、2つのバージョンがあり、1つのバージョンの数は多いですが、まったく役に立たないでしょう.次のコードです.
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 "";
}
}
}
テストgetResult()では値が得られません.具体的には、上記のコードを検討してみると、なぜか、完全に穴ですね.ここではあまり言いません.
もう一つのバージョン、つまり私が今使っている(ここではまずこの兄弟たちに感謝します.具体的な元の経路はすぐ下に貼られます)です.下には私のコードの元のコードがあります.私のところに問題があります.この問題があるのか、それとも私に問題があるのか、下にはどんな問題があるのか、どうやって解決したのか分かりません.
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); // byte
}
/**
* getWriter() , PrintWriter
*/
@Override
public PrintWriter getWriter() throws IOException {
try{
pwrite = new PrintWriter(new OutputStreamWriter(bytes, "utf-8"));
} catch(UnsupportedEncodingException e) {
e.printStackTrace();
}
return pwrite;
}
/**
* 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); // stream
}
}
}
HttpServeretResponseのパッケージクラスはフィルタでしか使用できないので、フィルタでしか実現できません.以下は私のフィルタのdoFilter()メソッドのコードです.
@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)) { // gzip
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("---------------- gzip -----------------");
} else { // gzip , gzip
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse resp = (HttpServletResponse) servletResponse;
ResponseWrapper mResp = new ResponseWrapper(resp); // resp
filterChain.doFilter(req, mResp);
byte[] bytes = mResp.getBytes(); //
System.out.println(" :" + bytes.length);
System.out.println(" :" + new String(bytes,"utf-8"));
ByteArrayOutputStream bout = new ByteArrayOutputStream();
GZIPOutputStream gzipOut = new GZIPOutputStream(bout); // GZIPOutputStream
gzipOut.write(bytes); // Gzip
gzipOut.flush();
gzipOut.close(); // bout
byte[] bts = bout.toByteArray();
System.out.println(" :" + bts.length);
resp.setHeader("Content-Encoding", "gzip"); //
resp.getOutputStream().write(bts); //
}
}
ここで上のコードを説明して、まずrequestリクエストがgzip圧縮を受け入れるかどうかを判断します.これはrequestのリクエストヘッダのaccept-encodingという属性に基づいて判断します.現在の各ブラウザはgzipをサポートしているので、gzip圧縮をしたい場合は、フロントエンドはこのリクエストヘッダを加えるだけで、バックエンドから返されるデータがgzip圧縮されたデータであれば、ブラウザは自動的に解凍します.
上のコードがgzip圧縮をサポートしていない場合は、処理しないで、通常の流れを下に進みます.gzip圧縮がサポートされている場合は、データ処理が必要です.
このコードを見てみましょう
filterChain.doFilter(req, mResp);
この方法は重要で、この方法の前の部分はすべてインタフェースを要求する前の部分で、もしあなたがインタフェースを呼び出す前に統一的に処理したいものがあれば、もちろんブロックのpreHandle()方法で処理することもできます.対応するこの方法の後の部分は,要求インタフェースに戻り値がある後の部分である.つまり今回はデータを圧縮する部分が必要です.
もちろん注意が必要なのはdoFilterの2番目のパラメータで、もともとサーブレットResponseオブジェクトだったのですが、今はデータを処理するためにResponseWrapperクラスを使ってサーブレットResponseをパッケージしているので、2番目のパラメータはResponseWrapperオブジェクトを伝えています.もちろん、servletRequestをパッケージしている場合は、では、最初のパラメータはservletRequestクラスをパッケージしたオブジェクトを伝えます.
次に、パッケージクラスのオブジェクトで返すデータを取得し、GZIPOutputStreamを使用してデータを圧縮するrespを使用する.getOutputStream().write(bts); 圧縮されたデータをresponseに書き込むには、当然、返されるリクエストヘッダにContent-Encoding(返されるコンテンツ符号化)をgzip形式とする必要があることを忘れてはいけない.
これによりresponseのデータを取り出して圧縮してフロントエンドに戻ることができます.もちろん圧縮する必要はありません.暗号化などの処理もできます.
上の流れの中で、私は1つの问题に出会って、注意しなければならなくて、あなた达が出会ったかどうか分かりませんが、上の流れはすべて正常に行って、データも取得して、圧縮も圧縮して、実行时间も印刷して、しかしフロントエンドはずっと応答の中で、つまり私达の応答の遅さ、私は见て、平均は30秒ぐらいで、これでは納得できない.
最初はフロントエンドがgzipデータを解凍する速度が遅すぎると思っていましたが、gzip関連コードを遮断して、データの戻りが同じように遅いので、gzip圧縮解凍排除しました.
そして一つだけ問題があります.それは私たちのパッケージクラスResponseWrapperに問題があります.debugを通じて、私たちがパッケージしたクラスの各方法の実行順序を発見しました.
まず、newオブジェクトの構築方法ResponseWrapper(HttpServertResponse response)メソッドを呼び出し、フィルタのdoFilterメソッドを実行するときにパッケージクラスのgetoutputStream()メソッドを呼び出して、定義したByteArrayOutputStream、すなわちbyttesにデータを書き込みます.次にgetBytes()メソッドを呼び出してbytesをbyte配列に変換して返します.これが戻りデータです.
上記の流れから,理論的には問題なく,実際には我々が望んでいるデータも得られ,これらの方法は実行速度も速く,どの部分にもカートンが住んでいないことが分かった.では、問題はどこにあるのでしょうか.私はネットで半日探しましたが、この方面の資料は少ないです.最後にブログで、このコードを書いたのは、データを書く前にResponseオブジェクトを使ってcontentLengthをチャージする必要があります.つまり次のコードです
response.setContentLength(-1);
ここで最初はどこにこのコードを入れるか考えていませんでしたが、フィルターの中で考えていましたが、入れるタイミングが間違っていたと思いました.その後、パッケージ類を見てみると、このコードを書いた兄たちがHttpServeretResponseオブジェクトを定義し、構造方法でも初期化していることがわかりました.しかし全文はこのresponseオブジェクトには用いられなかった.getoutputStream()を呼び出してbytesにデータを書き込む前にこのコードを付けたのではないかと思います.やってみましたが、本当にいいですね.これで問題が解決する.
今回の需要は、相応の緩やかな問題をどのように解決するかに1日かかりましたが、多くのものを収穫しました.だからここでコードの皆さんに感謝します.そして、短いですが、私の最終的な問題を解決したブログを書いた皆さんに感謝します.
次は2つのブログのアドレスです.http://blog.csdn.net/yy417168602/article/details/53534776 http://blog.csdn.net/qbian/article/details/53909778