将 InputStream 转换为 byte[] 的最有效方法? [复制]

Posted

技术标签:

【中文标题】将 InputStream 转换为 byte[] 的最有效方法? [复制]【英文标题】:Most efficient way to convert InputStream into byte[]? [duplicate] 【发布时间】:2013-04-15 22:29:15 【问题描述】:
ChannelBufferInputStream responseStream = (ChannelBufferInputStream) response.getBodyAsStream();
ArrayList<Byte> arrayList = new ArrayList<Byte>();
try 
    while (responseStream.available() > 0) 
        arrayList.add(responseStream.readByte());
    
 catch (IOException e) 
    e.printStackTrace();
    return internalServerError();

Iterator<Byte> iterator = arrayList.iterator();
byte[] bytes = new byte[arrayList.size()];
int i = 0;
while (iterator.hasNext()) 
    bytes[i++] = iterator.next();

在我的网络应用程序的每个页面加载时都会调用此代码。它似乎运行得很快,但有什么可以让它运行得更快吗?

编辑 - 使用字节数组输出流更新

ChannelBufferInputStream responseStream = (ChannelBufferInputStream) response.getBodyAsStream();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try 
    int read = responseStream.read();
    while (read != -1) 
        byteArrayOutputStream.write(read);
        read = responseStream.read();
    
 catch (IOException e) 
    e.printStackTrace();
    return internalServerError();

byte[] bytes = byteArrayOutputStream.toByteArray();
return ok(bytes).as(response.getHeader("Content-type"));

编辑 - 基准测试代码

ChannelBufferInputStream responseStream = (ChannelBufferInputStream) response.getBodyAsStream();
long t1 = System.nanoTime();

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try 
    int read = responseStream.read();
    while (read != -1) 
        byteArrayOutputStream.write(read);
        read = responseStream.read();
    
 catch (IOException e) 
    e.printStackTrace();
    return internalServerError();

byte[] bytes = byteArrayOutputStream.toByteArray();

long t2 = System.nanoTime();
System.out.println(t2-t1);
return ok(bytes).as(response.getHeader("Content-type"));

100+ 次请求后的平均时间 - 46873

ChannelBufferInputStream responseStream = (ChannelBufferInputStream) response.getBodyAsStream();
long t1 = System.nanoTime();

ArrayList<Byte> arrayList = new ArrayList<Byte>();
try 
    while (responseStream.available() > 0) 
        arrayList.add(responseStream.readByte());
    
 catch (IOException e) 
    e.printStackTrace();
    return internalServerError();

Iterator<Byte> iterator = arrayList.iterator();
byte[] bytes = new byte[arrayList.size()];
int i = 0;
while (iterator.hasNext()) 
    bytes[i++] = iterator.next();


long t2 = System.nanoTime();
System.out.println(t2-t1);
return ok(bytes).as(response.getHeader("Content-type"));

100+ 次请求后的平均时间 - 522848

long t1 = System.nanoTime();
byte[] bytes;
try 
    bytes = org.apache.commons.io.IOUtils.toByteArray(responseStream);
 catch (Exception e) 
    return internalServerError();


long t2 = System.nanoTime();
System.out.println(t2-t1);

100+ 次请求后的平均时间 - 45088

long t1 = System.nanoTime();
byte[] bytes;
try 
    bytes = sun.misc.IOUtils.readFully(responseStream, -1, true);
 catch (Exception e) 
    return internalServerError();


long t2 = System.nanoTime();
System.out.println(t2 - t1);

100+ 次请求后的平均时间 - 20180

【问题讨论】:

嗨,马特,我已经阅读了那篇文章。我正在寻找最高效率。 如果您对此感到担忧,您应该自己衡量和比较不同的实现。但是,既然您说“它似乎运行得很快”,如果您想让代码运行得更快,这听起来不像是瓶颈。 看看这个***.com/questions/6649100/… @TiagoAlmeida 在这里并不重要。 @MattBall 代码尚未部署到生产环境,但由于每次页面加载都会调用此代码,我希望它尽可能快地运行以缩短页面加载时间。 【参考方案1】:

是的。使用 ByteArrayOutputStream 而不是 ArrayList。然后从 InputStream 中读取字节块(不使用 available(),它几乎永远不会使用)并将这些块写入 ByteArrayOutputStream,直到 read() 方法返回 -1。然后在您的ByteArrayOutputStream 上调用 toByteArray()。

您可以使用 Guava 的 ByteStreams.toByteArray() 方法,它可以为您完成所有这些工作,或者您可以阅读它的源代码以更好地了解它是如何做到的。阅读IO tutorial 也可能有所帮助。

【讨论】:

谢谢,让我测试一下。我也会做一些基准测试 available() 返回可以不阻塞读取的字节数。它总是可以返回 0(默认情况下会这样做)。 速度有所提升,谢谢 知道为什么sun.misc.IOUtils.readFully 的运行速度是所有其他方法的两倍吗? ByteArrayOutputStream.toByteArray() 创建数据的新副本,这是不必要的。有没有办法避免这种情况?【参考方案2】:

为什么?这段代码完全等同于read(byte[]),只是它对整个数据做了两个额外的复制步骤。你不需要这些。一个简单的read(byte[]) 会快几倍。

available() 的使用也是无效的。您需要整个响应,而不仅仅是可以在没有阻塞的情况下读取的部分。你需要循环。

【讨论】:

谢谢,我现在正在修复这些问题。我会在几分钟后展示差异。 一个简单的 read() 不能保证(除非这个 ChannelBufferInputStream 实现带来这个保证)整个流被读取。也许这不是您真正的意思,但您需要一个读取循环,直到返回 -1。 @JBNizet 同意,澄清。【参考方案3】:

Apache Commons IO IOUtils.toByteArray 方法有什么问题?为此,多年来一直对其进行优化。

【讨论】:

嘿,我不想只为一个函数导入一个库。 好吧,随着时间的推移,您可能会在该库中使用更多函数,有什么害处?无论如何,它是开源的。如果您不想要全部内容,请阅读源代码,看看他们是如何做到的。 使用sun.misc.IOUtils怎么样?它内置在 Java 中,但我听说不要相信 sun 的库。 IOUtils.readFully(responseStream, -1, true); 虽然这给了我更快的结果。也许我会放弃并使用 apache commons... 这只是 Apache 的旧副本,AFAIK。如果您曾经在 IBM JVM 上运行过,它就不一定存在。 @sissonb 你能用理由、参考或引用来支持你关于 Suns 图书馆“不可信”的说法吗?你可能是对的,但如果没有证据,这是一个相当无益的说法。

以上是关于将 InputStream 转换为 byte[] 的最有效方法? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

在 Java 中,如何将 InputStream 转换为字节数组 (byte[])? [复制]

byte[]和InputStream的相互转换[转载]

byte[]和InputStream的相互转换

Byte[] 到 InputStream 或 OutputStream

Byte[] 到 InputStream 或 OutputStream

InputStream转换为String, byte[] data = new byte[1024]详解