GZIPInputStream se cierra prematuramente al descomprimir HTTPInputStream

Pregunta

Consulte la pregunta actualizada en la sección de edición a continuación.

Estoy tratando de descomprimir archivos GZIPed grandes (~ 300M) de Amazon S3 sobre la marcha usando GZIPInputStream pero solo genera una parte del archivo; sin embargo, si descargo al sistema de archivos antes de la descompresión, GZIPInputStream descomprimirá todo el archivo.

¿Cómo puedo hacer que GZIPInputStream descomprima todo HTTPInputStream y no solo la primera parte?

Lo que he probado

ver actualización en la sección de edición a continuación

Sospeché un problema HTTP, excepto que nunca se lanzan excepciones, GZIPInputStream devuelve un fragmento bastante consistente del archivo cada vez y, por lo que puedo decir, siempre se rompe en un límite de registro WET, aunque el límite que elige es diferente para cada uno URL (lo cual es muy extraño ya que todo se trata como una secuencia binaria, no se realiza ningún análisis de los registros WET en el archivo).

La pregunta más cercana que pude encontrar esGZIPInputStream se cierra prematuramente cuando se lee desde s3 La respuesta a esa pregunta fue que algunos archivos GZIP son en realidad múltiples archivos GZIP anexados y GZIPInputStream no se maneja tan bien. Sin embargo, si ese es el caso aquí, ¿por qué GZIPInputStream funcionaría bien en una copia local del archivo?

Código de demostración y salida

A continuación se muestra un fragmento de código de muestra que demuestra el problema que estoy viendo. Lo probé con Java 1.8.0_72 y 1.8.0_112 en dos computadoras Linux diferentes en dos redes diferentes con resultados similares. Espero que el recuento de bytes del HTTPInputStream descomprimido sea idéntico al recuento de bytes de la copia local descomprimida del archivo, pero el HTTPInputStream descomprimido es mucho más pequeño.

Salida
Testing URL https://commoncrawl.s3.amazonaws.com/crawl-data/CC-MAIN-2016-50/segments/1480698540409.8/wet/CC-MAIN-20161202170900-00009-ip-10-31-129-80.ec2.internal.warc.wet.gz
Testing HTTP Input Stream direct to GZIPInputStream
Testing saving to file before decompression
Read 87894 bytes from HTTP->GZIP
Read 448974935 bytes from HTTP->file->GZIP
Output from HTTP->GZIP saved to file testfile0.wet
------
Testing URL https://commoncrawl.s3.amazonaws.com/crawl-data/CC-MAIN-2016-50/segments/1480698540409.8/wet/CC-MAIN-20161202170900-00040-ip-10-31-129-80.ec2.internal.warc.wet.gz
Testing HTTP Input Stream direct to GZIPInputStream
Testing saving to file before decompression
Read 1772936 bytes from HTTP->GZIP
Read 451171329 bytes from HTTP->file->GZIP
Output from HTTP->GZIP saved to file testfile40.wet
------
Testing URL https://commoncrawl.s3.amazonaws.com/crawl-data/CC-MAIN-2016-50/segments/1480698541142.66/wet/CC-MAIN-20161202170901-00500-ip-10-31-129-80.ec2.internal.warc.wet.gz
Testing HTTP Input Stream direct to GZIPInputStream
Testing saving to file before decompression
Read 89217 bytes from HTTP->GZIP
Read 453183600 bytes from HTTP->file->GZIP
Output from HTTP->GZIP saved to file testfile500.wet
Código de muestra
import java.net.*;
import java.io.*;
import java.util.zip.GZIPInputStream;
import java.nio.channels.*;

public class GZIPTest {
    public static void main(String[] args) throws Exception {
        // Our three test files from CommonCrawl
        URL url0 = new URL("https://commoncrawl.s3.amazonaws.com/crawl-data/CC-MAIN-2016-50/segments/1480698540409.8/wet/CC-MAIN-20161202170900-00009-ip-10-31-129-80.ec2.internal.warc.wet.gz");
        URL url40 = new URL("https://commoncrawl.s3.amazonaws.com/crawl-data/CC-MAIN-2016-50/segments/1480698540409.8/wet/CC-MAIN-20161202170900-00040-ip-10-31-129-80.ec2.internal.warc.wet.gz");
        URL url500 = new URL("https://commoncrawl.s3.amazonaws.com/crawl-data/CC-MAIN-2016-50/segments/1480698541142.66/wet/CC-MAIN-20161202170901-00500-ip-10-31-129-80.ec2.internal.warc.wet.gz");

        /*
         * Test the URLs and display the results
         */
        test(url0, "testfile0.wet");
        System.out.println("------");
        test(url40, "testfile40.wet");
        System.out.println("------");
        test(url500, "testfile500.wet");
    }

    public static void test(URL url, String testGZFileName) throws Exception {
        System.out.println("Testing URL "+url.toString());

        // First directly wrap the HTTPInputStream with GZIPInputStream
        // and count the number of bytes we read
        // Go ahead and save the extracted stream to a file for further inspection
        System.out.println("Testing HTTP Input Stream direct to GZIPInputStream");
        int bytesFromGZIPDirect = 0;
        URLConnection urlConnection = url.openConnection();
        FileOutputStream directGZIPOutStream = new FileOutputStream("./"+testGZFileName);

        // FIRST TEST - Decompress from HTTPInputStream
        GZIPInputStream gzipishttp = new GZIPInputStream(urlConnection.getInputStream());

        byte[] buffer = new byte[1024];
        int bytesRead = -1;
        while ((bytesRead = gzipishttp.read(buffer, 0, 1024)) != -1) {
            bytesFromGZIPDirect += bytesRead;
            directGZIPOutStream.write(buffer, 0, bytesRead); // save to file for further inspection
        }
        gzipishttp.close();
        directGZIPOutStream.close();

        // Now save the GZIPed file locally
        System.out.println("Testing saving to file before decompression");
        int bytesFromGZIPFile = 0;
        ReadableByteChannel rbc = Channels.newChannel(url.openStream());
        FileOutputStream outputStream = new FileOutputStream("./test.wet.gz");
        outputStream.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
        outputStream.close();

        // SECOND TEST - decompress from FileInputStream
        GZIPInputStream gzipis = new GZIPInputStream(new FileInputStream("./test.wet.gz"));

        buffer = new byte[1024];
        bytesRead = -1;
        while((bytesRead = gzipis.read(buffer, 0, 1024)) != -1) {
            bytesFromGZIPFile += bytesRead;
        }
        gzipis.close();

        // The Results - these numbers should match but they don't
        System.out.println("Read "+bytesFromGZIPDirect+" bytes from HTTP->GZIP");
        System.out.println("Read "+bytesFromGZIPFile+" bytes from HTTP->file->GZIP");
        System.out.println("Output from HTTP->GZIP saved to file "+testGZFileName);
    }

}
Editar

Secuencia cerrada y canal asociado en el código de demostración según el comentario de @VGR.

ACTUALIZAR:

El problema parece ser algo específico del archivo. Saqué el archivo Common Crawl WET localmente (wget), lo descomprimí (gunzip 1.8), luego lo recomprimí (gzip 1.8) y lo volví a cargar en S3 y la descompresión sobre la marcha funcionó bien. Puede ver la prueba si modifica el código de muestra anterior para incluir las siguientes líneas:

// Original file from CommonCrawl hosted on S3
URL originals3 = new URL("https://commoncrawl.s3.amazonaws.com/crawl-data/CC-MAIN-2016-50/segments/1480698540409.8/wet/CC-MAIN-20161202170900-00009-ip-10-31-129-80.ec2.internal.warc.wet.gz");
// Recompressed file hosted on S3
URL rezippeds3 = new URL("https://s3-us-west-1.amazonaws.com/com.jeffharwell.commoncrawl.gziptestbucket/CC-MAIN-20161202170900-00009-ip-10-31-129-80.ec2.internal.warc.wet.gz");

test(originals3, "originalhost.txt");
test(rezippeds3, "rezippedhost.txt");

URL rezippeds3 apunta al archivo WET que descargué, descomprimí y recomprimí, luego volví a cargar en S3. Verá el siguiente resultado:

Testing URL https://commoncrawl.s3.amazonaws.com/crawl-data/CC-MAIN-2016-50/segments/1480698540409.8/wet/CC-MAIN-20161202170900-00009-ip-10-31-129-80.ec2.internal.warc.wet.gz
Testing HTTP Input Stream direct to GZIPInputStream
Testing saving to file before decompression
Read 7212400 bytes from HTTP->GZIP
Read 448974935 bytes from HTTP->file->GZIP
Output from HTTP->GZIP saved to file originals3.txt
-----
Testing URL https://s3-us-west-1.amazonaws.com/com.jeffharwell.commoncrawl.gziptestbucket/CC-MAIN-20161202170900-00009-ip-10-31-129-80.ec2.internal.warc.wet.gz
Testing HTTP Input Stream direct to GZIPInputStream
Testing saving to file before decompression
Read 448974935 bytes from HTTP->GZIP
Read 448974935 bytes from HTTP->file->GZIP
Output from HTTP->GZIP saved to file rezippeds3.txt

Como puede ver una vez que el archivo se volvió a comprimir, pude transmitirlo a través de GZIPInputStream y obtener el archivo completo. El archivo original todavía muestra el final prematuro habitual de la descompresión. Cuando descargué y cargué el archivo WET sin volver a comprimirlo, obtuve el mismo comportamiento de transmisión incompleto, por lo que definitivamente fue la recompresión lo que lo solucionó. También puse ambos archivos, el original y el recomprimido, en un servidor web tradicional de Apache y pude replicar los resultados, por lo que S3 no parece tener nada que ver con el problema.

Entonces. Tengo una nueva pregunta.

Nueva pregunta

¿Por qué un FileInputStream se comportaría de manera diferente a un HTTPInputStream al leer el mismo contenido? Si es exactamente el mismo archivo, ¿por qué?

nuevo GZIPInputStream (urlConnection.getInputStream ());

comportarse de manera diferente a

nuevo GZIPInputStream (nuevo FileInputStream ("./ test.wet.gz"));

?? ¿No es un flujo de entrada solo un flujo de entrada?

Respuestas a la pregunta(1)

Su respuesta a la pregunta