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;
}
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.
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.
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));
}
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);
}
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;
}
}
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
}
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.
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();
}
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() );
}
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();
}
}
}