2014-02-05 26 views
11

當我使用翻新1.4.1和okhttp 1.3.0在WS上激活gzip時遇到問題。翻新+ okhttp:檢索GZIPInputStream

RequestInterceptor requestInterceptor = new RequestInterceptor() { 
      @Override 
      public void intercept(RequestFacade request) { 
       request.addHeader("content-type", "application/json"); 
       request.addHeader("accept-encoding", "gzip"); // Here is the problem 
      } 
     }; 
RestAdapter restAdapter = new RestAdapter.Builder() 
      .setEndpoint(Constants.HOST) 
      .setLogLevel(RestAdapter.LogLevel.FULL) 
      .setRequestInterceptor(requestInterceptor) 
      .build(); 

如果我評論以下行request.addHeader("accept-encoding", "gzip");沒有問題,但如果Gzip已被激活,我得到一個錯誤(我的請求落在failure)。

這裏是我的logcat與request.addHeader("accept-encoding", "gzip");

1326    Retrofit D : HTTP/1.1 200 OK 
    1326    Retrofit D Cache-Control: public, max-age=600 
    1326    Retrofit D Content-Encoding: gzip 
    1326    Retrofit D Content-Length: 254 
    1326    Retrofit D Content-Type: application/json 
    1326    Retrofit D Date: Wed, 05 Feb 2014 20:22:26 GMT 
    1326    Retrofit D OkHttp-Received-Millis: 1391631746193 
    1326    Retrofit D OkHttp-Response-Source: NETWORK 200 
    1326    Retrofit D OkHttp-Selected-Transport: http/1.1 
    1326    Retrofit D OkHttp-Sent-Millis: 1391631745971 
    1326    Retrofit D Server: Apache 
    1326    Retrofit D Vary: Accept-Encoding 
    1326    Retrofit D X-Powered-By: PHP/5.3.3-7+squeeze18 
    1326    Retrofit D ������������}�?O�0��~����nHZOH0 �D�ù���?���~w.�:����=�{� 
           ����|A���=�V/~}o�)���&����<�`�6&��ѳ:��5�ke��V�WD�H� 
           ���ud�J5رyp��G�ːg�y�ʴ����Mxq<�#�Rb`Su�@�0��y��lr;�W�2�C3� 
           T��$���.� 
              ��xѥ���R 
                y���hmt����R����o����v��[email protected]� 
           4Y���� 
    1326    Retrofit D <--- END HTTP (254-byte body) 
    1326    System.err W retrofit.RetrofitError: retrofit.converter.ConversionException: com.google.gson.JsonSyntaxException: java.lang.Ille 
           galStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 
    1326    System.err W at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:408) 
    1326    System.err W at retrofit.RestAdapter$RestHandler.access$100(RestAdapter.java:262) 
    1326    System.err W at retrofit.RestAdapter$RestHandler$2.obtainResponse(RestAdapter.java:313) 
    1326    System.err W at retrofit.CallbackRunnable.run(CallbackRunnable.java:38) 
    1326    System.err W at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080) 
    1326    System.err W at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573) 
    1326    System.err W at retrofit.Platform$Android$2$1.run(Platform.java:136) 
    1326    System.err W at java.lang.Thread.run(Thread.java:841) 
    1326    System.err W Caused by: retrofit.converter.ConversionException: com.google.gson.JsonSyntaxException: java.lang.IllegalStateExcep 

我怎樣才能打開gzip的?提前

回答

26

THX只是省略了accept-encoding頭從您的代碼。 OkHttp會添加自己的accept-encoding標題,如果服務器使用gzip響應,那麼OkHttp會爲你默默解壓。

0

您需要使用Okhttp也支持握的方形。不知道您是否創建了自定義實例,或者默認情況下是否啓用了您應該檢查文檔。

2

運行到類似的問題後(​​在我的情況下,如果不添加任何Accept-Encoding標頭,它偶爾會失去解壓縮響應,並且還會留下Content-Encoding:gzip標頭,導致JSON解析器),並沒有明確的解決方法,我通過創建下面的委託客戶端實現手動啓用gzip進行Retrofit。它的效果很好,除了你可能不應該將它用於非常大的響應(例如> 250KB),因爲它們首先被複制到一個字節數組中。

public class GzippedClient implements Client { 

    private Client wrappedClient; 

    public GzippedClient(Client wrappedClient) { 
     this.wrappedClient = wrappedClient; 
    } 

    @Override 
    public Response execute(Request request) throws IOException { 
     Response response = wrappedClient.execute(request); 

     boolean gzipped = false; 
     for (Header h : response.getHeaders()) { 
      if (h.getName() != null && h.getName().toLowerCase().equals("content-encoding") && h.getValue() != null && h.getValue().toLowerCase().equals("gzip")) { 
       gzipped = true; 
       break; 
      } 
     } 

     Response r = null; 
     if (gzipped) { 
      InputStream is = null; 
      ByteArrayOutputStream bos = null; 

      try { 
       is = new BufferedInputStream(new GZIPInputStream(response.getBody().in())); 
       bos = new ByteArrayOutputStream(); 

       int b; 
       while ((b = is.read()) != -1) { 
        bos.write(b); 
       } 

       TypedByteArray body = new TypedByteArray(response.getBody().mimeType(), bos.toByteArray()); 
       r = new Response(response.getUrl(), response.getStatus(), response.getReason(), response.getHeaders(), body); 
      } finally { 
       if (is != null) { 
        is.close(); 
       } 
       if (bos != null) { 
        bos.close(); 
       } 
      } 
     } else { 
      r = response; 
     } 
     return r; 
    } 

} 

您還必須爲您的請求添加Accept-Encoding標頭,例如,通過使用RequestInterceptor

requestFacade.addHeader("Accept-Encoding", "gzip"); 

最後,你有你的現有客戶包裝成新GzippedClient,就像這樣:

restBuilder.setClient(new GzippedClient(new OkClient(okHttpClient))); 

就是這樣。現在你的數據將被壓縮。

編輯:似乎在OkHttp版本1.5.1中,一個錯誤(https://github.com/square/okhttp/pull/632)似乎已被修復與透明gzip有關,可能(或可能不會)我的初始問題的來源。如果是這樣,偶爾不會發生un-gzip失敗,儘管它發生得很少,我還無法證實這一點。無論哪種方式,如果你想依靠自己的,而不是透明的添加/刪除標題和gzip,那麼所描述的解決方案將工作。

2

如果你在OkHttp庫中檢查HttpEngine,那麼你可以找到下面的代碼,這意味着如果你手動添加「Accept-Encoding」:「gzip」頭部到請求中,那麼un-zip是你的責任。 /** * True if this client added an "Accept-Encoding: gzip" header field and is * therefore responsible for also decompressing the transfer stream. */ private boolean transparentGzip;

因此,如果您手動添加「Accept-Encoding」:「gzip」標題,那麼在獲得響應後,請執行下面的un-zip。

private static String readContentFromTypedInput(TypedInput typedInput){ 

     InputStreamReader isr = null; 
     BufferedReader br = null; 
     char[] cbuf = new char[512]; 
     StringWriter stringWriter = new StringWriter(); 

     try { 
      final InputStream in = typedInput.in(); 
      boolean isGzipped = GZipper.isGzippped(in); 
      if(isGzipped){ 
       return new String(GZipper.doUnZip(in)); 
      } 
      isr = new InputStreamReader(in); 
      br = new BufferedReader(isr); 
      while((br.read(cbuf))!= -1){ 
       stringWriter.write(cbuf); 
      } 
     } catch (IOException e) { 
      throw new InvalidTestCaseException("failed read received content.", e); 
     } finally{ 
      try{ 
       if(br != null) br.close(); 
      }catch(IOException e){ 
       //ignore 
      } 
     } 
     return stringWriter.toString().trim(); 
    } 

GZipper.java

public class GZipper{ 

public static final String DEFAULT_CHARSET = "utf-8"; 
private static final int BYTE_BLOCK_LENGTH = 1024; 

public static byte[] doZip(final String message){ 
    if(message == null || message.isEmpty()){ 
     throw new SystemFailedException("Fail to zip - given message is null or empty"); 
    } 
    byte[] gzippped = null; 
    try { 
     gzippped = doZip(message.getBytes(DEFAULT_CHARSET)); 
    } catch (Throwable e) { 
     throw new SystemFailedException(e.getMessage(), e); 
    } 
    return gzippped; 
} 

public static byte[] doZip(final byte[] unzippedMessageByte){ 
    validate(unzippedMessageByte, "Fail to zip - given bytes is null or empty"); 

    ByteArrayInputStream is = null; 
    ByteArrayOutputStream bos = null; 
    GZIPOutputStream gzip_os = null; 
    byte[] compressedBytes = null; 
    try{ 
     is = new ByteArrayInputStream(unzippedMessageByte); 
     bos = new ByteArrayOutputStream(); 
     gzip_os = new GZIPOutputStream(bos); 
     copy(is, gzip_os); 
     gzip_os.finish(); 
     compressedBytes = bos.toByteArray(); 
    }catch(IOException e){ 
     throw new SystemFailedException(e.getMessage(), e); 
    }finally{ 
     try{ 
      if(is != null){is.close();} 
      if(gzip_os != null){gzip_os.close();} 
      if(bos != null){bos.close();} 
     }catch(IOException e){ 
      //ignore 
     } 
    } 
    return compressedBytes; 
} 

public static String doUnZipToString(final byte[] gzippedMessage){ 
    validate(gzippedMessage, "Fail to unzip - given bytes is null or empty"); 
    byte[] gzippped = null; 
    String unzippedMessage = null; 
    try { 
     gzippped = doUnZip(gzippedMessage); 
     unzippedMessage = new String(gzippped, DEFAULT_CHARSET); 
    } catch (Throwable e) { 
     throw new SystemFailedException(e.getMessage(), e); 
    } 
    return unzippedMessage; 
} 

private static void validate(final byte[] bytes, String failedMessage) { 
    if(bytes == null || bytes.length == 0){ 
     throw new SystemFailedException(failedMessage); 
    } 
} 

public static byte[] doUnZip(InputStream in) { 
    if(!(in instanceof ByteArrayInputStream)){ 
     try { 
      return doUnZip(IOUtils.toByteArray(in)); 
     } catch (IOException e) { 
      throw new SystemFailedException(e.getMessage(), e); 
     } 
    } 

    ByteArrayOutputStream bos = null; 
    InputStream gzip_is = null; 
    byte[] bytes = null; 
    try{ 
     bos = new ByteArrayOutputStream(); 
     gzip_is = new GZIPInputStream(in); 
     copy(gzip_is,bos); 
     bytes = bos.toByteArray(); 
    }catch(IOException e){ 
     throw new SystemFailedException(e.getMessage(), e); 
    }finally{ 
     try{ 
      if(gzip_is != null) gzip_is.close(); 
      if(bos != null) bos.close(); 
     }catch(IOException e){ 
      //ignore 
     } 
    } 
    return bytes; 
} 

public static byte[] doUnZip(final byte[] zippedMessage){ 
    validate(zippedMessage, "Fail to unzip - given bytes is null or empty"); 
    ByteArrayInputStream is = null; 
    try{ 
     is = new ByteArrayInputStream(zippedMessage); 
     return doUnZip(is); 
    }finally{ 
     try{ 
      if(is != null) is.close(); 
     }catch(IOException e){ 
      //ignore 
     } 
    } 
} 

public static String doUnZip(File file){ 
    validate(file); 

    GZIPInputStream gzipInputStream = null; 
    StringWriter writer = null; 
    String result = ""; 

    try{ 
     byte[] buffer = new byte[BYTE_BLOCK_LENGTH]; 
     gzipInputStream = new GZIPInputStream(new FileInputStream(file)); 
     writer = new StringWriter(); 
     while((gzipInputStream.read(buffer)) > 0){ 
      writer.write(new String(buffer)); 
      writer.flush(); 
     } 
     result = writer.toString(); 
    }catch(IOException e){ 
     //do something to handle exception 
    } 
    finally{ 
     try{ 
      if(writer != null){writer.close();} 
      if(gzipInputStream != null){gzipInputStream.close();} 
     }catch(IOException e){ 
      //ignore 
     } 
    } 
    return result; 
} 

private static void validate(File file) { 
    if(file==null || !file.exists()){ 
     throw new SystemFailedException("Fail to unzip - file is not exist"); 
    } 
} 

private static void copy(InputStream in, OutputStream out)throws IOException { 
    byte[] buf = new byte[BYTE_BLOCK_LENGTH]; 
    int len = -1; 
    while ((len = in.read(buf, 0, buf.length)) != -1) { 
     out.write(buf, 0, len); 
    } 
} 

public static boolean isGzipped(byte[] input){ 
    return isGzippped(new ByteArrayInputStream(input)); 
} 

public static boolean isGzippped(InputStream in){ 
    boolean markSupported = in.markSupported(); 
    boolean result = false; 
    try { 
     if(markSupported){ 
      in.mark(0); 
      result = (readUShort(in) == GZIPInputStream.GZIP_MAGIC); 
      in.reset(); 
     } 
    } catch (Exception e) { 
     result = false; 
    } 
    return result; 
} 

private static int readUShort(InputStream in) throws IOException { 
    int b = readUByte(in); 
    return ((int)readUByte(in) << 8) | b; 
} 

/* 
* Reads unsigned byte. 
*/ 
private static int readUByte(InputStream in) throws IOException { 
    int b = in.read(); 
    if (b == -1) { 
     throw new EOFException(); 
    } 
    if (b < -1 || b > 255) { 
     b = 0; 
    } 
    return b; 
} 

}