有没有办法检查InputStream是否已被gzip压缩? 这是代码:
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;
}
}
我试过这种方式,但它没有按预期工作 - 从流中读取的值无效。 编辑: 添加了我用来压缩数据的方法:
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;
}
答案 0 :(得分:62)
这不是万无一失的,但它可能是最简单的,不依赖任何外部数据。像所有体面的格式一样,GZip也以一个神奇的数字开头,可以在不读取整个流的情况下快速检查。
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 file format specification)
更新:我刚刚发现GZIP_MAGIC
中还有一个名为GZipInputStream
的常量,其中包含此值,因此如果您真的想要,你可以使用它的低两个字节。
答案 1 :(得分:39)
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 spec明确指出。
更新:按照压缩源流的方式:这个比率检查非常......疯狂。摆脱它。相同的长度并不一定意味着字节是相同的。让它始终返回gzip压缩流,以便始终期望一个gzip压缩流,只需应用GZIPInputStream
而不需要讨厌的检查。
答案 2 :(得分:21)
我发现这个useful example提供了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)));
}
}
我成功测试了它:
@Test
public void testIsCompressed() {
assertFalse(util.isCompressed(originalBytes));
assertTrue(util.isCompressed(compressed));
}
答案 3 :(得分:8)
我认为这是检查字节数组是否格式化为gzip的最简单方法,它不依赖于任何HTTP实体或mime类型支持
public static boolean isGzipStream(byte[] bytes) {
int head = ((int) bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
return (GZIPInputStream.GZIP_MAGIC == head);
}
答案 4 :(得分:3)
以@biziclop的答案为基础 - 此版本使用GZIP_MAGIC标头,对空字节或单字节数据流也是安全的。
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;
}
}
答案 5 :(得分:2)
此功能在 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
}
答案 6 :(得分:1)
将原始流包装在BufferedInputStream中,然后将其包装在GZipInputStream中。 接下来尝试提取ZipEntry。如果这样可行,那就是一个zip文件。然后,您可以在BufferedInputStream中使用“mark”和“reset”,在检查后返回到流中的初始位置。
答案 7 :(得分:1)
如果您使用的是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();
}
答案 8 :(得分:0)
这是如何读取可以被gzip压缩的文件:
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();
}
}
}
答案 9 :(得分:0)
SimpleMagic是用于解析内容类型的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() );
}