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