web-dev-qa-db-fra.com

Comment vérifier si InputStream est Gzippé?

Existe-t-il un moyen de vérifier si InputStream a été compressé? Voici le code:

public static InputStream decompressStream(InputStream input) {
    try {
        GZIPInputStream gs = new GZIPInputStream(input);
        return gs;
    } catch (IOException e) {
        logger.info("Input stream not in the GZIP format, using standard format");
        return input;
    }
}

J'ai essayé de cette façon, mais cela ne fonctionne pas comme prévu - les valeurs lues dans le flux ne sont pas valides. EDIT: Ajout de la méthode que j'utilise pour compresser les données:

public static byte[] compress(byte[] content) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try {
        GZIPOutputStream gs = new GZIPOutputStream(baos);
        gs.write(content);
        gs.close();
    } catch (IOException e) {
        logger.error("Fatal error occured while compressing data");
        throw new RuntimeException(e);
    }
    double ratio = (1.0f * content.length / baos.size());
    if (ratio > 1) {
        logger.info("Compression ratio equals " + ratio);
        return baos.toByteArray();
    }
    logger.info("Compression not needed");
    return content;

}
50
voo

Ce n'est pas infaillible, mais c'est probablement le plus simple et ne repose sur aucune donnée externe. Comme tous les formats décents, GZip commence aussi par un nombre magique qui peut être rapidement vérifié sans lire le flux entier.

public static InputStream decompressStream(InputStream input) {
     PushbackInputStream pb = new PushbackInputStream( input, 2 ); //we need a pushbackstream to look ahead
     byte [] signature = new byte[2];
     int len = pb.read( signature ); //read the signature
     pb.unread( signature, 0, len ); //Push back the signature to the stream
     if( signature[ 0 ] == (byte) 0x1f && signature[ 1 ] == (byte) 0x8b ) //check if matches standard gzip magic number
       return new GZIPInputStream( pb );
     else 
       return pb;
}

(Source pour le nombre magique: spécification du format de fichier GZip )

Mise à jour: Je viens de découvrir qu'il existe également une constante appelée GZIP_MAGIC dans GZipInputStream qui contient cette valeur, donc si vous voulez vraiment , vous pouvez en utiliser les deux octets inférieurs.

63
biziclop

InputStream provient de HttpURLConnection # getInputStream ()

Dans ce cas, vous devez vérifier si HTTP Content-Encoding l'en-tête de réponse est égal à gzip.

URLConnection connection = url.openConnection();
InputStream input = connection.getInputStream();

if ("gzip".equals(connection.getContentEncoding())) {
    input = new GZIPInputStream(input);
}

// ...

Tout cela est clairement spécifié dans spécification HTTP .


Mise à jour : selon la façon dont vous avez compressé la source du flux: cette vérification du ratio est assez ... folle. Débarrassez-vous-en. La même longueur ne signifie pas nécessairement que les octets sont identiques. Laissez-le toujours retourner le flux compressé afin que vous puissiez toujours vous attendre à un flux compressé et simplement appliquer GZIPInputStream sans vérifications désagréables.

39
BalusC

J'ai trouvé ceci exemple utile qui fournit une implémentation propre de isCompressed():

/*
 * Determines if a byte array is compressed. The Java.util.Zip GZip
 * implementaiton does not expose the GZip header so it is difficult to determine
 * if a string is compressed.
 * 
 * @param bytes an array of bytes
 * @return true if the array is compressed or false otherwise
 * @throws Java.io.IOException if the byte array couldn't be read
 */
 public boolean isCompressed(byte[] bytes) throws IOException
 {
      if ((bytes == null) || (bytes.length < 2))
      {
           return false;
      }
      else
      {
            return ((bytes[0] == (byte) (GZIPInputStream.GZIP_MAGIC)) && (bytes[1] == (byte) (GZIPInputStream.GZIP_MAGIC >> 8)));
      }
 }

Je l'ai testé avec succès:

@Test
public void testIsCompressed() {
    assertFalse(util.isCompressed(originalBytes));
    assertTrue(util.isCompressed(compressed));
}
23
Aaron Roller

Je pense que c'est le moyen le plus simple de vérifier si un tableau d'octets est au format gzip ou non, cela ne dépend d'aucune entité HTTP ou prise en charge de type mime

public static boolean isGzipStream(byte[] bytes) {
      int head = ((int) bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
      return (GZIPInputStream.GZIP_MAGIC == head);
}
8
Oconnell

S'appuyant sur la réponse de @biziclop - cette version utilise l'en-tête GZIP_MAGIC et est en outre sans danger pour les flux de données vides ou à un octet.

public static InputStream maybeDecompress(InputStream input) {
    final PushbackInputStream pb = new PushbackInputStream(input, 2);

    int header = pb.read();
    if(header == -1) {
        return pb;
    }

    int b = pb.read();
    if(b == -1) {
        pb.unread(header);
        return pb;
    }

    pb.unread(new byte[]{(byte)header, (byte)b});

    header = (b << 8) | header;

    if(header == GZIPInputStream.GZIP_MAGIC) {
        return new GZIPInputStream(pb);
    } else {
        return pb;
    }
}
3
blue

Cette fonction fonctionne parfaitement bien en Java:

public static boolean isGZipped(File f) {   
    val raf = new RandomAccessFile(file, "r")
    return GZIPInputStream.GZIP_MAGIC == (raf.read() & 0xff | ((raf.read() << 8) & 0xff00))
}

Dans scala:

def isGZip(file:File): Boolean = {
   int gzip = 0
   RandomAccessFile raf = new RandomAccessFile(f, "r")
   gzip = raf.read() & 0xff | ((raf.read() << 8) & 0xff00)
   raf.close()
   return gzip == GZIPInputStream.GZIP_MAGIC
}
3
ypriverol

Enveloppez le flux d'origine dans un BufferedInputStream, puis enveloppez-le dans un GZipInputStream. Essayez ensuite d'extraire un ZipEntry. Si cela fonctionne, c'est un fichier Zip. Ensuite, vous pouvez utiliser "marquer" et "réinitialiser" dans le BufferedInputStream pour revenir à la position initiale dans le flux, après votre vérification.

1
Amir Afghani

Pas exactement ce que vous demandez, mais pourrait être une approche alternative si vous utilisez HttpClient:

private static InputStream getInputStream(HttpEntity entity) throws IOException {
  Header encoding = entity.getContentEncoding(); 
  if (encoding != null) {
     if (encoding.getValue().equals("gzip") || encoding.getValue().equals("Zip") ||      encoding.getValue().equals("application/x-gzip-compressed")) {
        return new GZIPInputStream(entity.getContent());
     }
  }
  return entity.getContent();
}
1
Richard H

SimpleMagic est une bibliothèque Java pour la résolution des types de contenu:

<!-- pom.xml -->
    <dependency>
        <groupId>com.j256.simplemagic</groupId>
        <artifactId>simplemagic</artifactId>
        <version>1.8</version>
    </dependency>

import com.j256.simplemagic.ContentInfo;
import com.j256.simplemagic.ContentInfoUtil;
import com.j256.simplemagic.ContentType;
// ...

public class SimpleMagicSmokeTest {

    private final static Logger log = LoggerFactory.getLogger(SimpleMagicSmokeTest.class);

    @Test
    public void smokeTestSimpleMagic() throws IOException {
        ContentInfoUtil util = new ContentInfoUtil();
        InputStream possibleGzipInputStream = getGzipInputStream();
        ContentInfo info = util.findMatch(possibleGzipInputStream);

        log.info( info.toString() );
        assertEquals( ContentType.GZIP, info.getContentType() );
    }
0
Abdull

Voici comment lire un fichier qui PEUT ÊTRE compressé:

private void read(final File file)
        throws IOException {
    InputStream stream = null;
    try (final InputStream inputStream = new FileInputStream(file);
            final BufferedInputStream bInputStream = new BufferedInputStream(inputStream);) {
        bInputStream.mark(1024);
        try {
            stream = new GZIPInputStream(bInputStream);
        } catch (final ZipException e) {
            // not gzipped OR not supported Zip format
            bInputStream.reset();
            stream = bInputStream;
        }
        // USE STREAM HERE
    } finally {
        if (stream != null) {
            stream.close();
        }
    }
}
0
TekTimmy