有人可以告诉我如何以尽可能少的创建时间克隆输入流吗?我需要多次克隆输入流以使用多种方法来处理IS。我已经尝试了三种方法,但由于某种原因或其他原因,事情无法正常进行。
方法1:
多亏了stackoverflow社区,我发现以下链接很有用,并将代码段合并到了我的程序中。
How to clone an 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);
方法2:
我还尝试使用BufferedInputStream克隆IS。这非常快(最慢的创建时间== 1ms。最快的== 0ms)。但是,在我发送is1待处理后,处理is2和is3的方法抛出了一个错误,表明没有要处理的东西,几乎就像下面的所有3个变量都引用了相同的IS一样。
is = getFileFromBucket(path,filename);
...
...
InputStream is1 = new BufferedInputStream(is);
InputStream is2 = new BufferedInputStream(is);
InputStream is3 = new BufferedInputStream(is);
方法3:
我认为编译器在骗我。对于上面的两个示例,我检查了is1的markSupported()。它返回了true,所以我认为我可以运行
is1.mark()
is1.reset()
要不就
is1.reset();
在将IS传递给我各自的方法之前。在以上两个示例中,我都收到一条错误消息,指出它是无效标记。
我现在没主意了,所以在此先感谢您能为我提供的任何帮助。
P.S.从我收到的人们的评论中,我需要澄清一些有关我的处境的事情:
1)该程序正在VM上运行
2)输入流是从另一种方法传递给我的。我不是从本地文件读取
3)输入流的大小未知
最佳答案
您可以创建某种自定义的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分钟更可能是硬件问题,而不是软件问题。我的7200rpm笔记本电脑硬盘可以在不到1秒的时间内完成操作。