1

gzip で圧縮された大きなテキスト ファイルを処理する必要があります。

InputStream is = new GZIPInputStream(new FileInputStream(path));
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = br.readLine()) != null) {
    someComputation();  
}

このコードは、ループ内で長い計算を行わない場合に機能します (そうする必要があります)。しかし、各行に数ミリ秒のスリープを追加するだけで、最終的にプログラムがクラッシュし、java.util.zip.ZipException が発生します。例外のメッセージは毎回異なります (「無効なリテラル/長さコード」、「無効なブロック タイプ」、「無効な格納ブロック長」)。
そのため、ストリームを十分にすばやく読み取っていないと、ストリームが破損しているようです。

問題なくファイルを解凍できます。Apache Commons Compress の GzipCompressorInputStream も試してみましたが、同じ結果が得られました。
ここで何が問題で、どうすれば解決できますか?

更新 1

私はこれを除外したと思っていましたが、さらにテストを行ったところ、問題はインターネットからのストリーミング ファイルに限定されていることがわかりました。

完全な例:

URL source = new URL(url);      
HttpURLConnection connection = (HttpURLConnection) source.openConnection();
connection.setRequestMethod("GET"); 
connection.setRequestProperty("Accept", "gzip, deflate"); 
BufferedReader br = new BufferedReader(new InputStreamReader(new GZIPInputStream(connection.getInputStream())));        
String line;
while ((line = br.readLine()) != null) { //exception is thrown here
    Thread.sleep(5);  
}

興味深いことに、行番号を出力すると、プログラムがクラッシュするのは常に同じ 4 ~ 5 行のうちの 1 行であることがわかりました。


update 2

実際のファイルを含む完全な例を次に示します。

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.zip.GZIPInputStream;


public class TestGZIPStreaming {

    public static void main(String[] args) throws IOException {

        URL source = new URL("http://tools.wmflabs.org/wikidata-exports/rdf/exports/20151130/wikidata-statements.nt.gz");      
        HttpURLConnection connection = (HttpURLConnection) source.openConnection();
        connection.setRequestMethod("GET"); 
        connection.setRequestProperty("Accept", "gzip, deflate"); 
        BufferedReader br = new BufferedReader(new InputStreamReader(new GZIPInputStream(connection.getInputStream())));       

        String line;
        int n = 0;

        while ((line = br.readLine()) != null) { //exception is thrown here
            Thread.sleep(10);  
            System.out.println(++n);
        }

    }

}

このファイルでは、90000 行付近でクラッシュが発生しています。

タイムアウトの問題を除外するために試しconnection.setReadTimeout(0)ましたが、効果はありませんでした。

おそらくネットワークの問題です。しかし、ブラウザでファイルをダウンロードできるので、それを処理する方法が必要です。

更新 3

Apache HttpClient を使って接続してみました。

HttpClient client = HttpClients.createDefault();
HttpGet get = new HttpGet("http://tools.wmflabs.org/wikidata-exports/rdf/exports/20151130/wikidata-statements.nt.gz");
get.addHeader("Accept-Encoding", "gzip");
HttpResponse response = client.execute(get);
BufferedReader br = new BufferedReader(new InputStreamReader(new GZIPInputStream(new BufferedInputStream(response.getEntity().getContent()))));

現在、次の例外が発生していますが、これはおそらくより役立つでしょう。

org.apache.http.ConnectionClosedException: Premature end of Content-Length delimited message body (expected: 3850131; received: 1581056
at org.apache.http.impl.io.ContentLengthInputStream.read(ContentLengthInputStream.java:180)
at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:137)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
at java.util.zip.InflaterInputStream.fill(InflaterInputStream.java:238)
at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:158)
at java.util.zip.GZIPInputStream.read(GZIPInputStream.java:117)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
at java.io.BufferedReader.readLine(BufferedReader.java:389)

繰り返しますが、ブラウザでファイルをダウンロードして問題なく解凍できるため、問題を処理する方法が必要です。

4

0 に答える 0