Android: для связи по протоколу HTTP следует использовать Accept-Encoding: gzip.


109

У меня HTTP-соединение с веб-сервером, запрашивающим данные JSON. Я хочу сжать этот поток данных с помощью Content-Encoding: gzip. Есть ли способ установить Accept-Encoding: gzipв моем HttpClient? Поиск gzipв справочниках Android не обнаруживает ничего, связанного с HTTP, как вы можете видеть здесь .


1
Android-WebRequest автоматически поддерживает GZIP и несжатые ответы, например, с помощью new WebRequest().get().to("http://www.example.com/").askForGzip(true).executeSync(). В частности, метод parseResponse (...) должен быть тем, что вы ищете.
caw

Ответы:


174

Вы должны использовать заголовки http, чтобы указать, что соединение может принимать данные в кодировке gzip, например:

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

Проверить ответ на кодировку содержимого:

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

7
Это отличный и очень полезный ответ со всеми необходимыми мне деталями. Большое спасибо. Один комментарий: вместо addHeader я использовал setHeader. Насколько я понимаю, это перезаписывает существующее "Accept-Encoding", если оно есть. Не уверен, какой подход правильный / лучший. Чтобы перезаписать существующий заголовок, чтобы убедиться, что он имеет правильное значение, или добавить его в случае, если параллельно могут быть другие заголовки Accept-Encoding.
znq

куда же тогда инстрим поставить?
Майки

7
это не gzip запрос, он только сообщает серверу, что вы можете принять gzip-ответ.
Джеффри Блаттман

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
Маркос Васконселос

1
AndroidHttpClient устарел на уровне API 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
По какой причине вы думаете, что это интереснее?
Gaurav Agarwal

3
@codingcrow Вот то, что вы ищете: в этом конкретном случае HTTP-клиент может выполнять сжатие GZIP прозрачного содержимого путем добавления двух перехватчиков протокола: перехватчика запросов, который добавляет заголовок Accept-Encoding: gzip ко всем исходящим запросам и ответ перехватчик, который автоматически расширяет сжатые объекты ответа, обертывая их классом-декоратором без сжатия. Использование перехватчиков протокола делает сжатие содержимого полностью прозрачным для потребителя интерфейса HttpClient.
Clauziere 03


GzipDecompressingEntity не может быть найден в android.jar (2.2)
imcaptor 08

1

Я не использовал GZip, но предполагаю, что вы должны использовать входной поток из вашего HttpURLConnectionили HttpResponseas GZIPInputStream, а не из какого-то конкретного другого класса.


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

Не отвечайте, если у вас нет ответа!
Martin.Martinsson

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.