Czytanie danych z (nie)gzipowych strumieni

0

Witam. Chodzi mi mianowicie o czytanie danych, które są przesyłane jako odpowiedź serwera w protokole HTTP - oczywiście danych skompresowanych gzipem.
Więc tak: wysyłam żądanie do serwera X, serwer X przesyła mi nagłówki odpowiedzi. Jednym z nich jest:
Content-Encoding: gzip
Teraz wiadomo, że odpowiedź trzeba zdekopresować przed odczytem.
Standardowo wystarczy czytać kolejne bajty wprost z InputStream. Wydawało mi się, że w tym szczególnym wypadku powinno się zrobić coś takiego:

InputStream is = ... // strumień wejścia, czytający dane z odpowiedzi serwera
GZIPInputStream gzip = new GZIPInputStream(is);

i czytać bajty ze zmiennej 'gzip'.
Niestety pojawił się wyjątek:
"Not in GZIP format" w GZIPInputStream.readHeader()
I teraz nie wiem co począć... dane ze strumienia na pewno są kompresowane, jednak co innego twierdzi GZIPInputStream

PS: najpierw czytam standardowo wszystkie nagłówki odpowiedzi wraz z CRLF na końcu. Dopiero wtedy tworzę GZIPInputStream i czytam ciało dokumentu.

0

spróbuj zcachować ciało dokumentu i dopiero taki obiekt wtłoczyć w gzipa.

0

A można jaśniej? Jakimi klasami, metodami można to zrobić?
Bardzo pomógłby mi fragment kodu.

Dzięki!

// I jeszcze raz ja. Teraz dopiero zauważyłem, że powodem, iż GZIPInputStream się buntuje jest to, że do ciała dokumentu przesyłanego z serwera dodawana jest na początku i na końcu pewna dodatkowa porcja bajtów.
W moim przypadku na początku znajduje się:
17(spacja)\r\n
na końcu
\r\n0\r\n
Po "wywaleniu" tych początkowych bajtów i bajtów końcowych strumień GZIP ładnie wszystko dekopresuje.

// I po raz kolejny raz ja. Nie wiem jak mogłem nie zauwazyć nagłówka:
Transfer-Encoding: chunked
w odpowiedzi serwera, który wskazuje na transmisję chunked :/ Liczba 17 (w systemie hex) na początku dokumentu wskazuje po prostu blok danych o 23 bajtach, a zero na końcu tego dokumentu o zakończeniu przesyłania danych.
Najważniejsze jednak to, że się sprawa wyjaśniła :P

0

I po raz kolejny ja. W związku z tym, że moje pytanie dotyczy kopresji postanowiłem nie zakładać nowego tematu (realizuje program taniego państwa ;-) ).
Tym razem sprawa tyczy się kompreji, a raczej dekompresji algorytmem deflate.
Jeśli w odpowiedzi serwera odnajdziemy taką linikę:
Content-Encoding: deflate
to będziemy musieli użyć właśnie dekompresji deflate. Z tym są jednak pewne problemy. Aby zdekopresować taki dokument najłatwiej posłużyć się klasą InflaterInputStream z paczki java.util.zip.
InflaterInputStream iis = new InflaterInputStream(is); // gdzie is to strumień odpowiedzi
Jednak na niektóych stronach wyskakuje błąd:
java.util.zip.DataFormatException: unknown compression method
W takich przypadkach trzeba użyć innego konstruktora klasy InflaterInputStream:
InflaterInputStream iis = new InflaterInputStream(is, new Inflater(true));
Jednak ten sposób zawodzi przy stronach z przypadku numer 1:
java.util.zip.DataFormatException: invalid stored block lengths
Najlepiej byłoby wiedzieć, który konstruktor InflaterInputStreama użyć mając do dyspozycji sam strumień odpowiedzi serwera. Tylko nie mam pojęcia jak to zrobić :-/ . Można by oczywiście użyć pierwszego konstruktora, jeśli ten spoób wyrzuci wyjątek, to należałoby użyć drugiego konstruktora. Pomysł skuteczny, ale nieładny...
Jakieś inne pomysły?

0

Tym razem sam sobie odpowiem :-P Zauważyłem, że skompresowany kod z jednego przypadku zaczyna się zawsze od bajtu 120 (znak 'x'), niezależnie od poziomu kompresji. Zauważyłem także, że kod z drugiego przypadku nigdy nie zaczyna się od bajtu 120, także niezależnie od poziomu kompresji. Wystarczyło więc napisać taki oto kod, może się komuś przyda...

public class DeflateInputStream extends InputStream
{
    protected InputStream is;
    
    public DeflateInputStream(InputStream is) throws IOException
    {
        if(is == null)
            throw new NullPointerException();
        this.is = is;
        this.is = new PushbackInputStream(this.is);
        int firstByte = this.is.read();
        ((PushbackInputStream)this.is).unread(firstByte);
        if(firstByte == 120)
            this.is = new InflaterInputStream(this.is);
        else
            this.is = new InflaterInputStream(this.is, new Inflater(true));
    }
    
    public int read() throws IOException
    {
        return this.is.read();
    }
}

// Jakoś wyszło 3 posty pod rząd, bardzo przepraszam.

1 użytkowników online, w tym zalogowanych: 0, gości: 1