私自身の質問に答える:
ここでは、2つの可能性があります。出力でのgunzip(GunzipOutputStream
Java APIによって提供されない使用など)または入力でのgunzip(GZIPInputStream
Java APIによって提供される使用など)に加えて、読み取り中にContent-Lengthを適用します。
私は両方を実行しましたが、後者の方が好きだと思います。なぜなら、a)バイトをPipedOutputStream
aに送り込むために別のスレッドを起動する必要がないからですPipedIOnputStream
。b)(当然の結果ですが)競合の脅威がないからです。 -条件およびその他の同期の問題。
まず、これが私の実装ですLimitedInputStream
。これにより、入力ストリームをラップして、読み取るデータの量に制限を適用できます。次よりも大きいContent-Length値をサポートするためにカウントをBigLimitedInputStream
使用するもあることに注意してください。BigInteger
Long.MAX_LONG
public class LimitedInputStream
extends InputStream
{
private long _limit;
private long _read;
private InputStream _in;
public LimitedInputStream(InputStream in, long limit)
{
_limit= limit;
_in = in;
_read = 0;
}
@Override
public int available()
throws IOException
{
return _in.available(); // sure?
}
@Override
public void close()
throws IOException
{
_in.close();
}
@Override
public boolean markSupported()
{
return false;
}
@Override
public int read()
throws IOException
{
int read = _in.read();
if(-1 == read)
return -1;
++_read;
if(_read > _limit)
return -1;
// throw new IOException("Read limit reached: " + _limit);
return read;
}
@Override
public int read(byte[] b)
throws IOException
{
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len)
throws IOException
{
// 'len' is an int, so 'max' is an int; narrowing cast is safe
int max = (int)Math.min((long)(_limit - _read), (long)len);
if(0 == max && len > 0)
return -1;
//throw new IOException("Read limit reached: " + _limit);
int read = _in.read(b, off, max);
_read += read;
// This should never happen
if(_read > _limit)
return -1;
//throw new IOException("Read limit reached: " + _limit);
return read;
}
@Override
public long skip(long n)
throws IOException
{
long max = Math.min((long)(_limit - _read), n);
if(0 == max)
return 0;
long read = _in.skip(max);
_read += read;
return read;
}
}
上記のクラスを使用してInputStream
から取得したものをラップHttpURLConnection
すると、既存のコードを簡略化できますContent-Length
。ヘッダーに記載されている正確なバイト数を読み取り、入力を出力に盲目的にコピーする必要がありました。次に、入力ストリーム(すでにラップされているLimitedInputStream
)をでラップしGZIPInputStream
て解凍し、(二重にラップされた)入力から出力にバイトを送ります。
それほど単純ではないルートは、私の元の行を追求することです:厄介なクラスを使用してOutputStreamをラップする:GunzipOutputStream
。GunzipOutputStream
内部スレッドを使用して、パイプされたストリームのペアを介してバイトをポンピングするを作成しました。それは醜いです、そしてそれはOpenRDFのGunzipOutputStream
コードに基づいています。私の方が少し簡単だと思います。
public class GunzipOutputStream
extends OutputStream
{
final private Thread _pump;
// Streams
final private PipedOutputStream _zipped; // Compressed bytes are written here (by clients)
final private PipedInputStream _pipe; // Compressed bytes are read (internally) here
final private OutputStream _out; // Uncompressed data is written here (by the pump thread)
// Internal state
private IOException _e;
public GunzipOutputStream(OutputStream out)
throws IOException
{
_zipped = new PipedOutputStream();
_pipe = new PipedInputStream(_zipped);
_out = out;
_pump = new Thread(new Runnable() {
public void run() {
InputStream in = null;
try
{
in = new GZIPInputStream(_pipe);
pump(in, _out);
}
catch (IOException e)
{
_e = e;
System.err.println(e);
_e.printStackTrace();
}
finally
{
try { in.close(); } catch (IOException ioe)
{ ioe.printStackTrace(); }
}
}
private void pump(InputStream in, OutputStream out)
throws IOException
{
long count = 0;
byte[] buf = new byte[4096];
int read;
while ((read = in.read(buf)) >= 0) {
System.err.println("===> Pumping " + read + " bytes");
out.write(buf, 0, read);
count += read;
}
out.flush();
System.err.println("===> Pumped a total of " + count + " bytes");
}
}, "GunzipOutputStream stream pump " + GunzipOutputStream.this.hashCode());
_pump.start();
}
public void close() throws IOException {
throwIOException();
_zipped.close();
_pipe.close();
_out.close();
}
public void flush() throws IOException {
throwIOException();
_zipped.flush();
}
public void write(int b) throws IOException {
throwIOException();
_zipped.write(b);
}
public void write(byte[] b) throws IOException {
throwIOException();
_zipped.write(b);
}
public void write(byte[] b, int off, int len) throws IOException {
throwIOException();
_zipped.write(b, off, len);
}
public String toString() {
return _zipped.toString();
}
protected void finish()
throws IOException
{
try
{
_pump.join();
_pipe.close();
_zipped.close();
}
catch (InterruptedException ie)
{
// Ignore
}
}
private void throwIOException()
throws IOException
{
if(null != _e)
{
IOException e = _e;
_e = null; // Clear the existing error
throw e;
}
}
}
繰り返しますが、これは機能しますが、かなり...壊れやすいようです。
LimitedInputStream
結局、とを使用するようにコードをリファクタリングし、を使用GZIPInputStream
しませんでしたGunzipOutputStream
。Java APIがを提供していればGunzipOutputStream
、それは素晴らしいことでした。しかし、そうではなく、「ネイティブ」のgunzipアルゴリズムを作成せずに、独自のアルゴリズムを実装するGunzipOutputStream
と、妥当性の限界が広がります。