如何在最短的时间内在Java中克隆输入流 [英] How to clone an inputstream in java in minimal time

查看:131
本文介绍了如何在最短的时间内在Java中克隆输入流的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有人可以告诉我如何以尽可能短的创建时间克隆输入流吗?我需要多次克隆输入流以使用多种方法来处理IS。我已经尝试了三种方法,但由于某种原因或某些原因而无法正常工作。

Can someone tell me how to clone an inputstream, taking as little creation time as possible? I need to clone an inputstream multiple times for multiple methods to process the IS. I've tried three ways and things don't work for one reason or another.

方法1:
多亏了stackoverflow社区,我发现了以下链接很有帮助,并将代码段纳入了我的程序。

Method #1: Thanks to the stackoverflow community, I found the following link helpful and have incorporated the code snippet in my program.

如何克隆InputStream?

但是,使用此代码最多可能需要一分钟(对于10MB的文件)才能创建

However, using this code can take up to one minute (for a 10MB file) to create the cloned inputstreams and my program needs to be as fast as possible.

    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克隆IS。这非常快(最慢的创建时间== 1ms。最快的== 0ms)。但是,在我发送is1待处理后,处理is2和is3的方法抛出了一个错误,表明没有要处理的东西,几乎就像下面的所有3个变量都引用了相同的IS。

Method #2: I also tried using BufferedInputStream to clone the IS. This was fast (slowest creation time == 1ms. fastest == 0ms). However, after I sent is1 to be processed, the methods processing is2 and is3 threw an error saying there was nothing to process, almost like all 3 variables below referenced the same IS.

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

方法3:
我认为编译器在骗我。我为上述两个示例检查了is1的markSupported()。它返回true,所以我认为我可以运行

Method #3: I think the compiler is lying to me. I checked markSupported() for is1 for the two examples above. It returned true so I thought I could run

    is1.mark() 
    is1.reset()

或者只是

    is1.reset();

,然后再将IS传递给我各自的方法。在上述两个示例中,我都会收到一个错误消息,指出它是无效标记。

before passing the IS to my respective methods. In both of the above examples, I get an error saying it's an invalid mark.

我现在没主意了,在此先感谢您能为我提供的任何帮助

I'm out of ideas now so thanks in advance for any help you can give me.

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

P.S. From the comments I've received from people, I need to clarify a couple things regarding my situation: 1) This program is running on a VM 2) The inputstream is being passed into me from another method. I'm not reading from a local file 3) The size of the inputstream is not known

推荐答案


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

您可以创建一种自定义 ReusableInputStream 类,其中您立即还写入内部 ByteArrayOutputStream 在第1次完整读取中,然后将其包装在 ByteBuffer 读取最后一个字节,并最终在随后的完整读取中重用相同的 ByteBuffer ,当达到限制时,该读取会自动翻转。

You could just create some kind of a custom ReusableInputStream class wherein you immediately also write to an internal ByteArrayOutputStream on the 1st full read, then wrap it in a ByteBuffer when the last byte is read and finally reuse the very same ByteBuffer on the subsequent full reads which get automatically flipped when limit is reached. This saves you from one full read as in your 1st attempt.

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

Here's a basic kickoff example:

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 [] 缓冲区)

(note that the actual job is performed in int read(byte[], int, int) instead of in int read() and thus it's expected to be faster when the caller itself is also streaming using a byte[] buffer)

流式传输,您可以按以下方式使用它:

You could use it as follows:

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分钟更可能是硬件问题,而不是软件问题。我的7200rpm笔记本电脑硬盘可以在不到1秒的时间内完成操作。

As to the performance, 1 minute per 10MB is more likely a hardware problem, not a software problem. My 7200rpm laptop harddisk does it in less than 1 second.

这篇关于如何在最短的时间内在Java中克隆输入流的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆