Android: HTTP-зв’язок повинен використовувати "Accept-Encoding: gzip"


109

Я повідомляю HTTP до веб-сервера, який вимагає даних JSON. Я хотів би стиснути цей потік даних Content-Encoding: gzip. Чи є спосіб я встановити Accept-Encoding: gzipв своєму HttpClient? Пошук gzipу Android References не відображає нічого, що стосується HTTP, як ви бачите тут .


1
Android-WebRequest підтримує автоматично GZIP та нестиснені відповіді, наприклад, за допомогою new WebRequest().get().to("http://www.example.com/").askForGzip(true).executeSync(). Зокрема, метод parseResponse (...) повинен бути тим, що ви шукаєте.
каре

Відповіді:


174

You should use http headers to indicate a connection can accept gzip encoded data, e.g:

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

Check response for content encoding:

InputStream instream = response.getEntity().getContent();
Header contentEncoding = response.getFirstHeader("Content-Encoding");
if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
    instream = new GZIPInputStream(instream);
}

7
That is a great and very helpful answer with all the details I needed. Thanks a lot. One comment: instead of addHeader I used setHeader. From what I understand this overwrites the existing "Accept-Encoding" if there is one. Not sure which approach is the right/better one. To overwrite an existing header to make sure it has the right value, or to add it in case that there can be other Accept-Encoding headers in parallel.
znq

where do you put the instream then?
Mikey

7
this does not gzip the request, it only tells the server that you can accept a gzip'd response.
Jeffrey Blattman

1
Для тих, хто також має проблеми з цим і запустити службу google, ось два питання, на які мені знадобилося досить багато часу: (1) для деяких служб Google потрібна рядок агента користувача, що надається клієнтом, щоб містити рядок, gzipщоб дійсно включити стиснення gzip. (2) майте на увазі, що сервер може не отримати відповіді, якщо він занадто малий ...
sven,

Моя проблема полягає в тому, що заголовок gzip ніде не помічено. Я використовую Wireshark, щоб нюхати мережу на стороні сервера, і я не бачу жодного сліду заголовка "gzip", який я додав в android ...
Тед

33

Якщо ви використовуєте API рівня 8 або вище, існує AndroidHttpClient .

Він має допоміжні методи, такі як:

public static InputStream getUngzippedContent (HttpEntity entity)

і

public static void modifyRequestToAcceptGzipResponse (HttpRequest request)

що призводить до набагато більш короткого коду:

AndroidHttpClient.modifyRequestToAcceptGzipResponse( request );
HttpResponse response = client.execute( request );
InputStream inputStream = AndroidHttpClient.getUngzippedContent( response.getEntity() );

Хороший підказок modifyRequest & getUngzipped, також, мені потрібно було скопіювати EntityUtils.toString (HttpEntity), щоб проаналізувати незахищений потік як String
Marcos Vasconcelos

1
AndroidHttpClient був застарілий на рівні 22-го.
Олексій Кучеренко

13

Я думаю, що зразок коду за цим посиланням є цікавішим: ClientGZipContentCompression.java

Вони використовують HttpRequestInterceptor та HttpResponseInterceptor

Зразок для запиту:

        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");
                }
            }

        });

Зразок для відповіді:

        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;
                        }
                    }
                }
            }

        });

1
Будь-яка причина, чому ви вважаєте, що це цікавіше?
Гаурав Агарвал

3
@codingcrow Ось що ви шукаєте: У цьому конкретному випадку HTTP-клієнт робить здатним до прозорого стиснення GZIP вмісту, додаючи два перехоплювача протоколу: перехоплювач запитів, який додає заголовку 'Accept-Encoding: gzip' до всіх вихідних запитів та відповіді. перехоплювач, який автоматично розширює об'єкти стислих відповідей, загортаючи їх у стислий клас декораторів. Використання перехоплювачів протоколів робить стиснення вмісту повністю прозорим для споживача інтерфейсу HttpClient.
clauziere


GzipDecompressingEntity не можна знайти в android.jar (2.2)
imcaptor

1

Я не використовував GZip, але я б припустив, що ви повинні використовувати вхідний потік з вашого HttpURLConnectionабо HttpResponseяк GZIPInputStream, а не з якогось іншого класу.


HTTPURLConnection увімкнено gzip за замовчуванням. Вам просто потрібно переконатися, що ваш веб-сервер може повертати gzip-сторінки. developer.android.com/reference/java/net/HttpURLConnection.html Мені вдалося повернути gzip сторінки з php, використовуючи ob_start ("ob_gzhandler");
metric152

Не відповідайте, коли у вас немає відповіді!
Мартін

0

У моєму випадку було так:

URLConnection conn = ...;
InputStream instream = conn.getInputStream();
String encodingHeader = conn.getHeaderField("Content-Encoding");
if (encodingHeader != null && encodingHeader.toLowerCase().contains("gzip"))
{
    instream = new GZIPInputStream(instream);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.