3

HTTP ContentEncoding "deflate"の処理に関連して、とストリームのOutputStream両方をインフレートするためにを使用する方法を知りたいです。理由は次のとおりです。gzipdeflate

Webサーバーからリソースをフェッチするクラスがあります(wgetJavaで考えてみてください)。私はそれを厳密に施行しています-応答の内容の長さ-その施行を維持したいと思います。したがって、私がやりたいのは、応答から特定のバイト数を読み取ることです(これはすでに実行しています)が、応答が圧縮されている場合は、より多くのバイトを生成します。

私はこれを次のdeflateような応答のために機能させています:

OutputStream out = System.out;
out = new InflateOutputStream(out);
// repeatedly:
out.write(compressedBytesFromResponse);

レスポンスでも同じことができるようにしたいと思いますがgzip、GunzipOutputStreamがないと、次に何をすべきかわかりません。

アップデート

私はこのようなものを作ることを考えてましたが、それは完全に正気ではないようでした。おそらく、それがOutputStream私のデータを膨らませるためにを使用する唯一の方法です。

4

3 に答える 3

0

の場合deflate、Javaには必要な処理を実行するInflaterOutputStreamがあります。圧縮された収縮データをフィードし、圧縮されていないデータを基になる出力ストリームに送信します。


のためgzipに...同等のものを見つけることができないようです。 InflaterOutputStreamのコンパニオンであるInflaterInputStream、には、GZipInputStreamすべてのヘッダーを処理するサブクラスがありますが、のサブクラスになる可能性のある同等の解凍出力ストリームクラスはありませんInflaterOutputStream

GZIP用に自分のサブクラスをInflaterOutputStream作成すると、ソースを見るとかなり毛むくじゃらに見えますGZipInputStream(ヘッダー、トレーラーなどを扱う)

パイプストリームを使用することは、2つの悪のうちの小さい方のようです。

于 2012-08-09T02:14:20.837 に答える
0

私自身の質問に答える:

ここでは、2つの可能性があります。出力でのgunzip(GunzipOutputStreamJava APIによって提供されない使用など)または入力でのgunzip(GZIPInputStreamJava APIによって提供される使用など)に加えて、読み取り中にContent-Lengthを適用します。

私は両方を実行しましたが、後者の方が好きだと思います。なぜなら、a)バイトをPipedOutputStreamaに送り込むために別のスレッドを起動する必要がないからですPipedIOnputStream。b)(当然の結果ですが)競合の脅威がないからです。 -条件およびその他の同期の問題。

まず、これが私の実装ですLimitedInputStream。これにより、入力ストリームをラップして、読み取るデータの量に制限を適用できます。次よりも大きいContent-Length値をサポートするためにカウントをBigLimitedInputStream使用するもあることに注意してください。BigIntegerLong.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をラップする:GunzipOutputStreamGunzipOutputStream内部スレッドを使用して、パイプされたストリームのペアを介してバイトをポンピングするを作成しました。それは醜いです、そしてそれは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と、妥当性の限界が広がります。

于 2012-08-09T17:15:46.687 に答える
-1

HttpURLConnectionを使用すると、これらすべてが自動的に行われます。

于 2012-08-08T23:35:01.527 に答える