分享

使用InputStream和OutputStream抽象在Java中快速压缩(ZIP)压缩.可能?

 印度阿三17 2019-06-10

我目前正在尝试编写一个自定义流代理(让我们以这种方式调用它),它可以改变给定输入流中的内容,并在必要时生成修改后的输出.这个要求确实是必要的,因为有时我必须修改我的应用程序中的流(例如,在运行中真正压缩数据).以下类非常简单,它使用内部缓冲.

private static class ProxyInputStream extends InputStream {

    private final InputStream iStream;
    private final byte[] iBuffer = new byte[512];

    private int iBufferedBytes;

    private final ByteArrayOutputStream oBufferStream;
    private final OutputStream oStream;

    private byte[] oBuffer = emptyPrimitiveByteArray;
    private int oBufferIndex;

    ProxyInputStream(InputStream iStream, IFunction<OutputStream, ByteArrayOutputStream> oStreamFactory) {
        this.iStream = iStream;
        oBufferStream = new ByteArrayOutputStream(512);
        oStream = oStreamFactory.evaluate(oBufferStream);
    }

    @Override
    public int read() throws IOException {
        if ( oBufferIndex == oBuffer.length ) {
            iBufferedBytes = iStream.read(iBuffer);
            if ( iBufferedBytes == -1 ) {
                return -1;
            }
            oBufferIndex = 0;
            oStream.write(iBuffer, 0, iBufferedBytes);
            oStream.flush();
            oBuffer = oBufferStream.toByteArray();
            oBufferStream.reset();
        }
        return oBuffer[oBufferIndex  ];
    }

}

假设我们还有一个示例测试输出流,它只是在每个写入字节(“abc” – >“a b c”)之前添加一个空格字符,如下所示:

private static class SpacingOutputStream extends OutputStream {

    private final OutputStream outputStream;

    SpacingOutputStream(OutputStream outputStream) {
        this.outputStream = outputStream;
    }

    @Override
    public void write(int b) throws IOException {
        outputStream.write(' ');
        outputStream.write(b);
    }

}

以下测试方法:

private static void test(final boolean useDeflater) throws IOException {
    final FileInputStream input = new FileInputStream(SOURCE);
    final IFunction<OutputStream, ByteArrayOutputStream> outputFactory = new IFunction<OutputStream, ByteArrayOutputStream>() {
        @Override
        public OutputStream evaluate(ByteArrayOutputStream outputStream) {
            return useDeflater ? new DeflaterOutputStream(outputStream) : new SpacingOutputStream(outputStream);
        }
    };
    final InputStream proxyInput = new ProxyInputStream(input, outputFactory);
    final OutputStream output = new FileOutputStream(SOURCE   ".~"   useDeflater);
    int c;
    while ( (c = proxyInput.read()) != -1 ) {
        output.write(c);
    }
    output.close();
    proxyInput.close();
}

此测试方法只是读取文件内容并将其写入另一个流,这可能会以某种方式进行修改.如果测试方法使用useDeflater = false运行,则预期的方法可以正常工作.但是如果在useDeflater设置的情况下调用测试方法,它的行为非常奇怪,并且几乎不写任何内容(如果省略标题78 9C).我怀疑deflater类的设计可能不符合我喜欢使用的方法,但我一直认为ZIP格式和deflate压缩设计是为了在飞行中工作.

可能我在某种程度上对于deflate压缩算法的细节是错误的.我真正想念的是什么?也许可能有另一种方法来编写“流代理”,以完全按照我希望它的工作方式行事……我如何仅仅使用流来限制数据的压缩?

提前致谢.

UPD:以下基本版本与deflater和inflater相当不错:

public final class ProxyInputStream<OS extends OutputStream> extends InputStream {

private static final int INPUT_BUFFER_SIZE = 512;
private static final int OUTPUT_BUFFER_SIZE = 512;

private final InputStream iStream;
private final byte[] iBuffer = new byte[INPUT_BUFFER_SIZE];
private final ByteArrayOutputStream oBufferStream;
private final OS oStream;
private final IProxyInputStreamListener<OS> listener;

private byte[] oBuffer = emptyPrimitiveByteArray;
private int oBufferIndex;
private boolean endOfStream;

private ProxyInputStream(InputStream iStream, IFunction<OS, ByteArrayOutputStream> oStreamFactory, IProxyInputStreamListener<OS> listener) {
    this.iStream = iStream;
    oBufferStream = new ByteArrayOutputStream(OUTPUT_BUFFER_SIZE);
    oStream = oStreamFactory.evaluate(oBufferStream);
    this.listener = listener;
}

public static <OS extends OutputStream> ProxyInputStream<OS> proxyInputStream(InputStream iStream, IFunction<OS, ByteArrayOutputStream> oStreamFactory, IProxyInputStreamListener<OS> listener) {
    return new ProxyInputStream<OS>(iStream, oStreamFactory, listener);
}

@Override
public int read() throws IOException {
    if ( oBufferIndex == oBuffer.length ) {
        if ( endOfStream ) {
            return -1;
        } else {
            oBufferIndex = 0;
            do {
                final int iBufferedBytes = iStream.read(iBuffer);
                if ( iBufferedBytes == -1 ) {
                    if ( listener != null ) {
                        listener.afterEndOfStream(oStream);
                    }
                    endOfStream = true;
                    break;
                }
                oStream.write(iBuffer, 0, iBufferedBytes);
                oStream.flush();
            } while ( oBufferStream.size() == 0 );
            oBuffer = oBufferStream.toByteArray();
            oBufferStream.reset();
        }
    }
    return !endOfStream || oBuffer.length != 0 ? (int) oBuffer[oBufferIndex  ] & 0xFF : -1;
}

}

解决方法:

我不相信DeflaterOutputStream.flush()做任何有意义的事情. deflater将累积数据,直到它有东西写出到底层流.强制剩余数据输出的唯一方法是调用DeflaterOutputStream.finish().但是,这对您当前的实现不起作用,因为在您完成写作之前无法调用完成.

实际上编写压缩流并在同一个线程中读取它实际上非常困难.在RMIIO项目中,我实际上是这样做的,但是你需要一个任意大小的中间输出缓冲区(你基本上需要将数据推入,直到另一端压缩出来的东西,然后你就可以读取它).您可以使用该项目中的某些util类来完成您想要执行的操作.

来源:http://www./content-1-235351.html

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多