如何检查InputStream是否被压缩?

有什么办法来检查InputStream是否已被gzipped? 代码如下:

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; } } 

我试过这种方式,但它不能按预期工作 – 从stream中读取的值是无效的。 编辑:添加了我用来压缩数据的方法:

 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; } 

这不是万无一失的,但它可能是最简单的,不依赖于任何外部数据。 像所有体面的格式一样,GZip也以一个魔术数字开头,可以在不读取整个stream的情况下快速检查。

 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; } 

(来源为幻数: GZip文件格式说明 )

更新:我刚刚发现在GZipInputStream中也有一个叫GZIP_MAGICGZipInputStream ,它包含了这个值,所以如果你真的想要的话,可以使用它的较低的两个字节。

InputStream来自HttpURLConnection#getInputStream()

在这种情况下,您需要检查HTTP Content-Encoding响应头是否等于gzip

 URLConnection connection = url.openConnection(); InputStream input = connection.getInputStream(); if ("gzip".equals(connection.getContentEncoding())) { input = new GZIPInputStream(input); } // ... 

这一切都清楚地在HTTP规范中指定。


更新 :按照如何压缩stream源:这个比率检查是相当…疯狂。 摆脱它。 相同的长度不一定意味着字节是相同的。 让它总是返回gzippedstream,这样你总是可以期待一个gzippedstream,只需要应用GZIPInputStream而不用讨厌的检查。

我发现这个有用的例子提供了一个干净的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))); } } 

我testing成功:

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

我相信这是检查一个字节数组是否是gzip格式的最简单的方法,它不依赖于任何HTTP实体或MIMEtypes支持

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

不完全是你在问什么,但如果您使用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(); } 

将原始stream封装在BufferedInputStream中,然后将其封装在GZipInputStream中。 接下来尝试提取一个ZipEntry。 如果这个工作,它是一个zip文件。 然后,您可以使用BufferedInputStream中的“标记”和“重置”返回到stream中的初始位置。

这个函数在Java中工作得很好:

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

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 } 

build立在@biziclop的答案上 – 该版本使用GZIP_MAGIC头,另外对于空字节或单字节数据stream是安全的。

 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; } } 

这是如何读取可以被压缩的文件:

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

SimpleMagic是用于parsing内容types的Java库:

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