如何在最短的时间内在java中克隆输入流

Cla*_*ied 6 java clone inputstream bufferedinputstream

有人可以告诉我如何克隆输入流,尽可能少的创建时间?我需要多次克隆输入流以便多种方法来处理IS.我尝试了三种方法,因为某种原因,事情不起作用.

方法#1:感谢stackoverflow社区,我发现以下链接很有帮助,并将代码片段合并到我的程序中.

如何克隆InputStream?

但是,使用此代码最多可能需要一分钟(对于10MB文件)来创建克隆的输入流,并且我的程序需要尽可能快.

    int read = 0;
    byte[] bytes = new byte[1024*1024*2];

    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    while ((read = is.read(bytes)) != -1)
        bos.write(bytes,0,read);
    byte[] ba = bos.toByteArray();

    InputStream is1 = new ByteArrayInputStream(ba);
    InputStream is2 = new ByteArrayInputStream(ba);
    InputStream is3 = new ByteArrayInputStream(ba);
Run Code Online (Sandbox Code Playgroud)

方法#2:我也尝试使用BufferedInputStream来克隆IS.这很快(创建时间最慢== 1ms.最快== 0ms).但是,在我发送is1进行处理之后,方法处理is2和is3引发了一个错误,说没有什么可以处理,几乎就像下面引用相同的IS的所有3个变量一样.

    is = getFileFromBucket(path,filename);
    ...
    ...
    InputStream is1 = new BufferedInputStream(is);
    InputStream is2 = new BufferedInputStream(is);
    InputStream is3 = new BufferedInputStream(is);
Run Code Online (Sandbox Code Playgroud)

方法#3:我认为编译器对我说谎.我在上面两个例子中检查了is1的markSupported().它返回true,所以我以为我可以跑了

    is1.mark() 
    is1.reset()
Run Code Online (Sandbox Code Playgroud)

要不就

    is1.reset();
Run Code Online (Sandbox Code Playgroud)

在将IS传递给我各自的方法之前.在上面的两个例子中,我收到一个错误,说它是无效标记.

我现在没有想法,所以提前感谢你能给我的任何帮助.

PS从我收到的人们的评论中,我需要澄清一些与我的情况有关的事情:1)这个程序在VM上运行2)输入流从另一个方法传递给我.我不是从本地文件中读取3)输入流的大小未知

Bal*_*usC 6

如何克隆输入流,尽可能少的创建时间?我需要多次克隆输入流以便多种方法来处理IS

您可以创建某种自定义ReusableInputStream类,其中您还可以在第一次完整读取时立即写入内部 ByteArrayOutputStream,然后在ByteBuffer读取最后一个字节时将其包装在最后一个字节中,最后ByteBuffer在后续完全读取时重复使用相同的自动翻转达到限制时.这样可以避免您在第一次尝试时完全阅读.

这是一个基本的启动示例:

public class ReusableInputStream extends InputStream {

    private InputStream input;
    private ByteArrayOutputStream output;
    private ByteBuffer buffer;

    public ReusableInputStream(InputStream input) throws IOException {
        this.input = input;
        this.output = new ByteArrayOutputStream(input.available()); // Note: it's resizable anyway.
    }

    @Override
    public int read() throws IOException {
        byte[] b = new byte[1];
        read(b, 0, 1);
        return b[0];
    }

    @Override
    public int read(byte[] bytes) throws IOException {
        return read(bytes, 0, bytes.length);
    }

    @Override
    public int read(byte[] bytes, int offset, int length) throws IOException {
        if (buffer == null) {
            int read = input.read(bytes, offset, length);

            if (read <= 0) {
                input.close();
                input = null;
                buffer = ByteBuffer.wrap(output.toByteArray());
                output = null;
                return -1;
            } else {
                output.write(bytes, offset, read);
                return read;
            }
        } else {
            int read = Math.min(length, buffer.remaining());

            if (read <= 0) {
                buffer.flip();
                return -1;
            } else {
                buffer.get(bytes, offset, read);
                return read;
            }
        }

    }

    // You might want to @Override flush(), close(), etc to delegate to input.
}
Run Code Online (Sandbox Code Playgroud)

(请注意,实际作业是在in int read(byte[], int, int)而不是in中执行的int read(),因此当调用者本身也使用byte[]缓冲区进行流式传输时,预计会更快)

您可以按如下方式使用它:

InputStream input = new ReusableInputStream(getFileFromBucket(path,filename));
IOUtils.copy(input, new FileOutputStream("/copy1.ext"));
IOUtils.copy(input, new FileOutputStream("/copy2.ext"));
IOUtils.copy(input, new FileOutputStream("/copy3.ext"));
Run Code Online (Sandbox Code Playgroud)

至于性能,每10MB 1分钟更可能是硬件问题,而不是软件问题.我的7200rpm笔记本电脑硬盘在不到1秒的时间内完成.


Ste*_*n C 3

但是,使用此代码可能需要长达一分钟(对于 10MB 文件)来创建克隆的输入流,并且我的程序需要尽可能快。

复制流需要时间,并且(通常)这是克隆流的唯一方法。除非缩小问题的范围,否则性能几乎没有机会得到显着提高。

以下是可以改进的几种情况:

  • 如果您事先知道流中的字节数,那么您可以直接读入最终的字节数组。

  • 如果您知道数据来自文件,则可以为该文件创建内存映射缓冲区。

但根本问题是移动大量字节需要时间。事实上,一个 10Mb 文件需要 1 分钟(使用问题中的代码)表明真正的瓶颈根本不在 Java 中。