Как проверить, является ли InputStream Gzipped?
Есть ли способ проверить, был ли 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;
}
Ответы
Ответ 1
Это не надежный, но он, вероятно, самый простой и не зависит от каких-либо внешних данных. Как и все достойные форматы, 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)
Обновление: Я только что подтвердил, что в GZipInputStream
есть константа с именем GZIP_MAGIC
, которая содержит это значение, поэтому если вы действительно хотите, вы можете использовать нижние два байта.
Ответ 2
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.
Обновить: в соответствии с тем, как вы сжали источник потока: эта проверка отношения довольно... безумная. Избавиться от этого. Такая же длина не обязательно означает, что байты одинаковы. Пусть он всегда возвращает поток gzipped, так что вы всегда можете ожидать gzipped-поток и просто применять GZIPInputStream
без отвратительных проверок.
Ответ 3
Я нашел этот полезный пример, который обеспечивает чистую реализацию 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));
}
Ответ 4
Я считаю, что это самый простой способ проверить, является ли массив байтов gzip отформатированным или нет, он не зависит от поддержки любого типа HTTP или mime
public static boolean isGzipStream(byte[] bytes) {
int head = ((int) bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
return (GZIPInputStream.GZIP_MAGIC == head);
}
Ответ 5
Оберните исходный поток в BufferedInputStream, а затем оберните его в GZipInputStream.
Затем попробуйте извлечь ZipEntry. Если это работает, это zip файл. Затем вы можете использовать "mark" и "reset" в BufferedInputStream для возврата к исходной позиции в потоке после проверки.
Ответ 6
Не совсем то, что вы просите, но может быть альтернативным подходом, если вы используете 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();
}
Ответ 7
Эта функция отлично работает в 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
}
Ответ 8
Основываясь на ответе @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;
}
}
Ответ 9
Вот как читать файл, который может быть gzipped:
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();
}
}
}
Ответ 10
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() );
}