如何克隆在Java在最短的时间的InputStream(How to clone an inputs

2019-08-02 12:46发布

谁能告诉我如何克隆一个InputStream,以尽可能少的创建时间可能吗? 我需要一个InputStream多次克隆的多种方法来处理IS。 我已经试过三种方式,事情不会因为某种原因或其他工作。

方法1:多亏了社区的计算器,我发现下面的链接有用,在我的计划已纳入的代码片段。

如何克隆一个InputStream?

然而,使用此代码可能需要长达一分钟(一个10MB的文件)来创建克隆inputstreams和我的程序必须尽可能快。

    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);

方法2:用我的BufferedInputStream克隆也试过。 这是快(慢创建时间1ms的==。最快== 0毫秒)。 不过,我送IS1后要处理的方法处理IS2和IS3抛出一个错误,说没有什么处理,几乎像下面提到的同样是所有3个变量。

    is = getFileFromBucket(path,filename);
    ...
    ...
    InputStream is1 = new BufferedInputStream(is);
    InputStream is2 = new BufferedInputStream(is);
    InputStream is3 = new BufferedInputStream(is);

方法3:我觉得编译器是在骗我。 我检查的markSupported()为IS1为上面的两个例子。 所以我想我可以运行它返回真

    is1.mark() 
    is1.reset()

要不就

    is1.reset();

前传递是我的各自的方法。 在上述两个例子中,我得到一个错误,说这是一个无效的标志。

我的想法现在这么预先感谢任何帮助,您可以给我。

PS从我从人收到的意见,我需要澄清有关我的情况有两件事情:1)该程序在虚拟机上运行2)InputStream中被传递到我的另一种方法。 我不是从本地文件读取3)InputStream的大小是未知

Answer 1:

如何克隆一个InputStream,以尽可能少的创建时间可能吗? 我需要一个InputStream多次克隆的多种方法来处理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.
}

(注意,实际工作中执行int read(byte[], int, int)中,而不是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"));

至于性能,10MB每1分钟时更可能是硬件问题,不是软件问题。 我的7200转的笔记本硬盘做它在不到1秒。



Answer 2:

然而,使用此代码可能需要长达一分钟(一个10MB的文件)来创建克隆inputstreams和我的程序必须尽可能快。

那么复制流需要时间,(一般)是克隆流的唯一途径。 除非你收紧问题的范围,很少有机会,性能可以提高显著。

这里有一对夫妇的情况下,其中的改进是可能的:

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

  • 如果你知道将数据从一个文件来,你可以创建该文件的内存映射缓冲区。

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



Answer 3:

关于你提到的第一种方法中,一个由在把你的所有字节在ByteArrayOutputStream:

  • 首先,这种方法会消耗大量的内存。 如果不确保您的JVM与分配足够的内存开始,它需要你流的处理过程中为动态内存,这是费时。
  • 您的ByteArrayOutputStream最初与32个字节的缓冲区中创建。 每次尝试的时候把东西在里面,如果它没有现有的字节数组创建一个新的更大的阵列和旧的字节被复制到新的适应。 既然你每次使用2MB输入,你正迫使ByteArrayOutputStream一遍又一遍的复制它的数据转换成更大的阵列,增加了其在2MB每次数组的大小。
  • 由于旧的阵列是垃圾,很可能他们的记忆是由垃圾收集器,使您的复制过程更慢回收。
  • 也许你应该使用指定的初始缓冲区大小构造函数定义ByArrayOutputStream。 更准确的说,你设置的大小更快的过程应该是因为中间少的副本是必需的。

您第二种方法是假的,你不能装点不同的其他流中相同的输入流和期望的事情工作。 由于字节由一个流被消耗,内流被排出为好,且不能提供其它流用准确的数据。

之前我向我的答案,让我问,你有其他的方法希望接收一个单独的线程运行的输入流的副本? 因为如果是这样,这听起来像是对的PipedOutputStream和PipedInputStream的工作?



Answer 4:

你打算单独的方法来并行或按顺序运行? 如果顺序,我看不出有任何理由要克隆的输入流,所以我必须假设你打算分拆的线程来管理每个流。

我不是一个计算机附近,现在来测试这一点,但我想你会更好读块输入,都说1024个字节,然后推那些块(或块的阵列复印件)到您的与输入流输出流连接到他们的线头。 有读者阻止如果没有可用数据,等等。



文章来源: How to clone an inputstream in java in minimal time