58

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;

}
4

10 に答える 10

76

これは絶対確実というわけではありませんが、おそらく最も簡単で、外部データに依存しません。すべての適切な形式と同様に、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ファイルフォーマット仕様

更新:この値を含むGZIP_MAGICinと呼ばれる定数もあることがわかったので、本当に必要な場合は、その下位 2 バイトを使用できます。GZipInputStream

于 2011-01-27T16:24:42.543 に答える
40

InputStream は HttpURLConnection#getInputStream() から取得されます

その場合、HTTPContent-Encoding応答ヘッダーが と等しいかどうかを確認する必要がありますgzip

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

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

// ...

これはすべてHTTP 仕様で明確に指定されています。


更新:ストリームのソースを圧縮する方法によると、この比率チェックはかなり...非常識です。それを取り除く。同じ長さは、必ずしもバイトが同じであることを意味しません。常にgzipされたストリームを返すようにして、常にgzipされたストリームを期待し、GZIPInputStream厄介なチェックなしで適用できるようにします。

于 2011-01-27T15:58:00.700 に答える
27

:のクリーンな実装を提供するこの便利な例を見つけました。isCompressed()

/*
 * Determines if a byte array is compressed. The java.util.zip GZip
 * implementation 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)
 {
      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));
}
于 2011-12-23T21:28:19.980 に答える
1

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() );
    }
于 2016-09-28T16:59:22.090 に答える
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();
}
于 2011-01-27T15:54:40.640 に答える
1

元のストリームを BufferedInputStream にラップしてから、それを GZipInputStream にラップします。次に、ZipEntry の抽出を試みます。これが機能する場合、それは zip ファイルです。次に、チェック後に BufferedInputStream で「マーク」と「リセット」を使用して、ストリームの最初の位置に戻ることができます。

于 2011-01-27T15:50:57.460 に答える