Contents

将InputStream转换为字节数组和ByteBuffer

1. 概述

在这个快速教程中,我们将了解如何将InputStream转换为byte[]ByteBuffer——首先使用纯 Java,然后使用 Guava 和 Commons IO。

2. 转换为字节数组

让我们看一下从简单的输入流中获取字节数组*。字节数组的重要方面是它可以对存储在内存中的每个 8 位(一个字节)值进行索引(快速)访问。因此,您可以操纵这些字节来控制每一位。我们将看看如何将简单的输入流转换为byte[]* ——首先使用纯 Java,然后使用GuavaApache Commons IO

2.1.使用纯 Java 进行转换

让我们从一个专注于处理固定大小流的 Java 解决方案开始:

@Test
public void givenUsingPlainJavaOnFixedSizeStream_whenConvertingAnInputStreamToAByteArray_thenCorrect() 
  throws IOException {
    InputStream is = new ByteArrayInputStream(new byte[] { 0, 1, 2 });
    byte[] targetArray = new byte[is.available()];
    is.read(targetArray);
}

在缓冲流的情况下——我们正在处理缓冲流并且不知道底层数据的确切大小,我们需要使实现更加灵活:

@Test
public void givenUsingPlainJavaOnUnknownSizeStream_whenConvertingAnInputStreamToAByteArray_thenCorrect() 
  throws IOException {
    InputStream is = new ByteArrayInputStream(new byte[] { 0, 1, 2, 3, 4, 5, 6 }); // not really known
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    int nRead;
    byte[] data = new byte[4];
    while ((nRead = is.read(data, 0, data.length)) != -1) {
        buffer.write(data, 0, nRead);
    }
    buffer.flush();
    byte[] targetArray = buffer.toByteArray();
}

从 Java 9 开始,我们可以使用专用的readNbytes方法实现相同的目的:

@Test
public void givenUsingPlainJava9OnUnknownSizeStream_whenConvertingAnInputStreamToAByteArray_thenCorrect() 
  throws IOException {
    InputStream is = new ByteArrayInputStream(new byte[] { 0, 1, 2, 3, 4, 5, 6 });
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    int nRead;
    byte[] data = new byte[4];
    while ((nRead = is.readNBytes(data, 0, data.length)) != 0) {
        System.out.println("here " + nRead);
        buffer.write(data, 0, nRead);
    }
    buffer.flush();
    byte[] targetArray = buffer.toByteArray();
}

这两种方法之间的区别非常微妙。

第一个是read(byte[] b, int off, int len) ,从输入流中读取最多len个字节的数据,而第二个readNBytes(byte[] b, int off, int len) , 准确读取请求的字节数**。

此外,如果输入流中没有更多可用数据,则read返回*-1*。然而,readNbytes总是返回读入缓冲区的实际字节数。

我们还可以一次读取所有字节:

@Test
public void
  givenUsingPlainJava9_whenConvertingAnInputStreamToAByteArray_thenCorrect()
  throws IOException {
    InputStream is = new ByteArrayInputStream(new byte[] { 0, 1, 2 });
    byte[] data = is.readAllBytes();
}

2.2. 使用Guava进行转换

现在让我们看一下基于 Guava 的简单解决方案——使用方便的 ByteStreams 实用程序类:

@Test
public void givenUsingGuava_whenConvertingAnInputStreamToAByteArray_thenCorrect() 
  throws IOException {
    InputStream initialStream = ByteSource.wrap(new byte[] { 0, 1, 2 }).openStream();
    
    byte[] targetArray = ByteStreams.toByteArray(initialStream);
}

2.3. 使用 Commons IO 进行转换

最后——一个使用 Apache Commons IO 的简单解决方案:

@Test
public void givenUsingCommonsIO_whenConvertingAnInputStreamToAByteArray_thenCorrect() 
  throws IOException {
    ByteArrayInputStream initialStream = new ByteArrayInputStream(new byte[] { 0, 1, 2 });
    
    byte[] targetArray = IOUtils.toByteArray(initialStream);
}

IOUtils.toByteArray()方法在内部缓冲输入,因此在需要缓冲时**无需使用BufferedInputStream实例。**

3. 转换为ByteBuffer

现在,让我们看一下从InputStream中获取ByteBuffer 。**当我们需要在内存中进行快速和直接的低级 I/O 操作时,**这很有用。

使用与上述部分相同的方法,我们将看看如何将InputStream转换为ByteBuffer - 首先使用纯 Java,然后使用 Guava 和 Commons IO。

3.1. 使用纯 Java 进行转换

在字节流的情况下——我们知道底层数据的确切大小。让我们使用ByteArrayInputStream#available方法将字节流读入ByteBuffer

@Test
public void givenUsingCoreClasses_whenByteArrayInputStreamToAByteBuffer_thenLengthMustMatch() 
  throws IOException {
    byte[] input = new byte[] { 0, 1, 2 };
    InputStream initialStream = new ByteArrayInputStream(input);
    ByteBuffer byteBuffer = ByteBuffer.allocate(3);
    while (initialStream.available() > 0) {
        byteBuffer.put((byte) initialStream.read());
    }
    assertEquals(byteBuffer.position(), input.length);
}

3.2. 使用Guava进行转换

现在让我们看一个简单的基于 Guava 的解决方案——使用方便的ByteStreams实用程序类:

@Test
public void givenUsingGuava__whenByteArrayInputStreamToAByteBuffer_thenLengthMustMatch() 
  throws IOException {
    InputStream initialStream = ByteSource
      .wrap(new byte[] { 0, 1, 2 })
      .openStream();
    byte[] targetArray = ByteStreams.toByteArray(initialStream);
    ByteBuffer bufferByte = ByteBuffer.wrap(targetArray);
    while (bufferByte.hasRemaining()) {
        bufferByte.get();
    }
    assertEquals(bufferByte.position(), targetArray.length);
}

在这里,我们使用带有方法hasRemaining的 while 循环来显示将所有字节读入ByteBuffer 的不同方法。否则,断言将失败,因为ByteBuffer索引位置将为零。

3.3. 使用 Commons IO 进行转换

最后——使用 Apache Commons IO 和IOUtils类:

@Test
public void givenUsingCommonsIo_whenByteArrayInputStreamToAByteBuffer_thenLengthMustMatch() 
  throws IOException {
    byte[] input = new byte[] { 0, 1, 2 };
    InputStream initialStream = new ByteArrayInputStream(input);
    ByteBuffer byteBuffer = ByteBuffer.allocate(3);
    ReadableByteChannel channel = newChannel(initialStream);
    IOUtils.readFully(channel, byteBuffer);
    assertEquals(byteBuffer.position(), input.length);
}