Android: komunikacja HTTP powinna używać „Accept-Encoding: gzip”

109

Mam komunikację HTTP z serwerem WWW żądającą danych JSON. Chciałbym skompresować ten strumień danych za pomocą Content-Encoding: gzip. Czy istnieje sposób, w jaki mogę ustawić Accept-Encoding: gzipw moim HttpClient? Wyszukiwanie gzipw odwołaniach do systemu Android nie powoduje wyświetlenia niczego związanego z protokołem HTTP, jak widać tutaj .

znq
źródło
1
Android-WebRequest automatycznie obsługuje GZIP i nieskompresowane odpowiedzi, np new WebRequest().get().to("http://www.example.com/").askForGzip(true).executeSync(). Z. W szczególności metoda parseResponse (...) powinna być tym, czego szukasz.
caw

Odpowiedzi:

174

Powinieneś użyć nagłówków http, aby wskazać, że połączenie może akceptować dane zakodowane w formacie gzip, np .:

HttpUriRequest request = new HttpGet(url);
request.addHeader("Accept-Encoding", "gzip");
// ...
httpClient.execute(request);

Sprawdź odpowiedź pod kątem kodowania treści:

InputStream instream = response.getEntity().getContent();
Header contentEncoding = response.getFirstHeader("Content-Encoding");
if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
    instream = new GZIPInputStream(instream);
}
Bakhtiyor
źródło
7
To świetna i bardzo pomocna odpowiedź, zawierająca wszystkie potrzebne mi szczegóły. Wielkie dzięki. Jeden komentarz: zamiast addHeader użyłem setHeader. Z tego, co rozumiem, nadpisuje to istniejące „Akceptuj kodowanie”, jeśli takie istnieje. Nie jestem pewien, które podejście jest właściwe / lepsze. Aby nadpisać istniejący nagłówek, aby upewnić się, że ma odpowiednią wartość, lub dodać go w przypadku, gdy równolegle mogą istnieć inne nagłówki Accept-Encoding.
znq
gdzie w takim razie umieszczasz in-stream?
Mikey,
7
nie powoduje to zgzipowania żądania, a jedynie informuje serwer, że możesz zaakceptować odpowiedź z programu gzip.
Jeffrey Blattman
1
Dla każdego, kto ma również problem z uruchomieniem tego dla usług Google, oto dwa problemy, których znalezienie zajęło mi trochę czasu: (1) niektóre usługi Google wymagają, aby ciąg agenta użytkownika dostarczony przez klienta zawierał ciąg, gzipaby naprawdę włączyć kompresja gzip. (2) pamiętaj, że serwer może nie spakować odpowiedzi, jeśli jest za mała ...
sven
Mój problem polega na tym, że nigdzie nie widać nagłówka gzip. Używam Wireshark wąchać sieci po stronie serwera, a nie widzę śladu „gzip” nagłówku, że dodany w android ...
Ted
33

Jeśli używasz interfejsu API na poziomie 8 lub wyższym, istnieje AndroidHttpClient .

Posiada metody pomocnicze, takie jak:

public static InputStream getUngzippedContent (HttpEntity entity)

i

public static void modifyRequestToAcceptGzipResponse (HttpRequest request)

prowadząc do znacznie bardziej zwięzłego kodu:

AndroidHttpClient.modifyRequestToAcceptGzipResponse( request );
HttpResponse response = client.execute( request );
InputStream inputStream = AndroidHttpClient.getUngzippedContent( response.getEntity() );
Volker Neumann
źródło
Niezła wskazówka dla modifiedRequest & getUngzipped, musiałem również skopiować EntityUtils.toString (HttpEntity), aby przeanalizować rozpakowany strumień jako String
Marcos Vasconcelos
1
AndroidHttpClient został wycofany na poziomie interfejsu API 22.
Alex Kucherenko
13

Myślę, że próbka kodu pod tym linkiem jest bardziej interesująca: ClientGZipContentCompression.java

Używają HttpRequestInterceptor i HttpResponseInterceptor

Próbka na żądanie:

        httpclient.addRequestInterceptor(new HttpRequestInterceptor() {

            public void process(
                    final HttpRequest request,
                    final HttpContext context) throws HttpException, IOException {
                if (!request.containsHeader("Accept-Encoding")) {
                    request.addHeader("Accept-Encoding", "gzip");
                }
            }

        });

Próbka odpowiedzi:

        httpclient.addResponseInterceptor(new HttpResponseInterceptor() {

            public void process(
                    final HttpResponse response,
                    final HttpContext context) throws HttpException, IOException {
                HttpEntity entity = response.getEntity();
                Header ceheader = entity.getContentEncoding();
                if (ceheader != null) {
                    HeaderElement[] codecs = ceheader.getElements();
                    for (int i = 0; i < codecs.length; i++) {
                        if (codecs[i].getName().equalsIgnoreCase("gzip")) {
                            response.setEntity(
                                    new GzipDecompressingEntity(response.getEntity()));
                            return;
                        }
                    }
                }
            }

        });
Niqo
źródło
1
Czy jest jakiś powód, dla którego uważasz, że jest bardziej interesujący?
Gaurav Agarwal,
3
@codingcrow Oto, czego szukasz: W tym konkretnym przypadku klient HTTP jest zdolny do kompresji przezroczystej zawartości GZIP poprzez dodanie dwóch przechwytywaczy protokołów: przechwytywacza żądań, który dodaje nagłówek „Accept-Encoding: gzip” do wszystkich wychodzących żądań i odpowiedzi przechwytywacz, który automatycznie rozszerza skompresowane jednostki odpowiedzi, opakowując je nieskompresowaną klasą dekoratora. Użycie przechwytywaczy protokołów sprawia, że ​​kompresja treści jest całkowicie przezroczysta dla konsumenta interfejsu HttpClient.
clauziere
Źródło
clauziere,
GzipDecompressingEntity nie można znaleźć w android.jar (2,2)
imcaptor
1

Nie używałem GZip, ale zakładam, że powinieneś użyć strumienia wejściowego z twojej HttpURLConnectionlub HttpResponseas GZIPInputStream, a nie jakiejś konkretnej innej klasy.

Dimitar Dimitrov
źródło
W protokole HTTPURLConnection gzip jest domyślnie włączony. Musisz tylko upewnić się, że serwer WWW może zwracać strony gzip. developer.android.com/reference/java/net/HttpURLConnection.html Udało mi się zwrócić strony gzip z php za pomocą ob_start ("ob_gzhandler");
metric152
Nie odpowiadaj, jeśli nie masz odpowiedzi!
Martin.Martinsson
0

W moim przypadku było tak:

URLConnection conn = ...;
InputStream instream = conn.getInputStream();
String encodingHeader = conn.getHeaderField("Content-Encoding");
if (encodingHeader != null && encodingHeader.toLowerCase().contains("gzip"))
{
    instream = new GZIPInputStream(instream);
}
kospol
źródło