在Java中将InputStream转换为字节数组

1001

我该如何将整个InputStream读入字节数组中?


18
请参考以下链接了解如何将 byte[] 转换为 InputStream:https://dev59.com/questions/AHI95IYBdhLWcg3w7CnL。 - David d C e Freitas
35个回答

15

如果您不想使用Apache commons-io库,可以使用sun.misc.IOUtils类中的此代码片段。与使用ByteBuffers的常用实现相比,它近乎快了一倍:

public static byte[] readFully(InputStream is, int length, boolean readAll)
        throws IOException {
    byte[] output = {};
    if (length == -1) length = Integer.MAX_VALUE;
    int pos = 0;
    while (pos < length) {
        int bytesToRead;
        if (pos >= output.length) { // Only expand when there's no room
            bytesToRead = Math.min(length - pos, output.length + 1024);
            if (output.length < pos + bytesToRead) {
                output = Arrays.copyOf(output, pos + bytesToRead);
            }
        } else {
            bytesToRead = output.length - pos;
        }
        int cc = is.read(output, pos, bytesToRead);
        if (cc < 0) {
            if (readAll && length != Integer.MAX_VALUE) {
                throw new EOFException("Detect premature EOF");
            } else {
                if (output.length != pos) {
                    output = Arrays.copyOf(output, pos);
                }
                break;
            }
        }
        pos += cc;
    }
    return output;
}

这是一个有点奇怪的解决方案,“length”是数组长度的上限。如果您知道长度,您只需要:byte[] output = new byte[length]; is.read(output);(但请参见我的答案) - Luke Hutchison
@luke-hutchison,就像我之前所说的那样,这是sun.misc.IOUtils的解决方案。在大多数情况下,您无法预先知道InputStream的大小,因此如果(length == -1)length = Integer.MAX_VALUE;适用。即使给定的长度大于InputStream的长度,此解决方案也可以正常工作。 - Kristian Kraljic
@LukeHutchison 如果你知道长度,可以用几行代码处理。如果你看每个答案,每个人都在抱怨长度不确定。最终一个标准的答案出现了,可以在Java 7 Android中使用,而且不需要任何外部库。 - Csaba Toth

12
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
while (true) {
    int r = in.read(buffer);
    if (r == -1) break;
    out.write(buffer, 0, r);
}

byte[] ret = out.toByteArray();

10
Input Stream is ...
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int next = in.read();
while (next > -1) {
    bos.write(next);
    next = in.read();
}
bos.flush();
byte[] result = bos.toByteArray();
bos.close();

然而,通常操作系统已经为此缓冲了足够的内容,对于较小的文件来说这不是一个巨大的问题。这并不像硬盘头会逐个字节地读取(硬盘是一个带有磁性编码信息的旋转玻璃盘,有点像我们用于保存数据的奇怪图标:P)。 - Maarten Bodewes
7
大多数设备都有一种块传输方式,因此并不是每个read()都会导致实际的设备访问。然而,每字节进行一次OS调用已足以降低性能。在执行该代码之前将InputStream包装在BufferedInputStream中可以减少OS调用并显著缓解性能缺陷,但该代码仍将从一个缓冲区进行不必要的手动复制工作到另一个缓冲区。 - Holger

9

@Adamski:您可以完全避免缓冲区。

代码来自http://www.exampledepot.com/egs/java.io/File2ByteArray.html(是的,它非常冗长,但需要的内存空间仅为其他解决方案的一半。)

// Returns the contents of the file in a byte array.
public static byte[] getBytesFromFile(File file) throws IOException {
    InputStream is = new FileInputStream(file);

    // Get the size of the file
    long length = file.length();

    // You cannot create an array using a long type.
    // It needs to be an int type.
    // Before converting to an int type, check
    // to ensure that file is not larger than Integer.MAX_VALUE.
    if (length > Integer.MAX_VALUE) {
        // File is too large
    }

    // Create the byte array to hold the data
    byte[] bytes = new byte[(int)length];

    // Read in the bytes
    int offset = 0;
    int numRead = 0;
    while (offset < bytes.length
           && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
        offset += numRead;
    }

    // Ensure all the bytes have been read in
    if (offset < bytes.length) {
        throw new IOException("Could not completely read file "+file.getName());
    }

    // Close the input stream and return bytes
    is.close();
    return bytes;
}

7
取决于事先了解尺寸大小。 - stolsvik
2
当然可以,但是他们应该知道图片的大小:“我想读取一张图片”。 - pihentagy
1
如果您知道大小,那么Java会为您提供代码。请查看我的答案或在Google上搜索“DataInputStream”及其readFully方法。 - dermoritz
如果 offset < bytes.length,则应添加 is.close(),否则如果抛出异常,InputStream 将不会被关闭。 - Jared Rummler
3
最好使用try-with-resources。 - pihentagy
问题是关于InputStream的。你只知道大小,因为你也有File对象。但是,某些第三方库可以为您提供InputStream。或者,您可能希望编写适用于文件、网络流和模拟的逻辑。 - Vlasec

5

Java 9将为您提供一个很棒的方法:

InputStream in = ...;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
in.transferTo( bos );
byte[] bytes = bos.toByteArray();

4
这个和 InputStram.readAllBytes() 的区别是什么?InputStram.readAllBytes() 是一行代码的形式。 - Slava Semushin
ByteArrayOutputStream 中肯定有很多数组调整大小的操作,接着是数据的完全复制。 - Maarten Bodewes

4
我们发现将S3对象转换为ByteArray时,部分AWS交易存在延迟。
注:S3对象为PDF文档(最大大小为3 mb)。
我们正在使用选项#1(org.apache.commons.io.IOUtils)来将S3对象转换为ByteArray。我们已注意到S3提供了内置的IOUtils方法来将S3对象转换为ByteArray,请您确认什么是避免延迟的最佳方法来将S3对象转换为ByteArray。
选项#1:
import org.apache.commons.io.IOUtils;
is = s3object.getObjectContent();
content =IOUtils.toByteArray(is);

选项#2:

import com.amazonaws.util.IOUtils;
is = s3object.getObjectContent();
content =IOUtils.toByteArray(is);

如果我们有其他更好的方法将S3对象转换为字节数组,请告诉我。


2

我知道现在已经很晚了,但是我认为这里有一个更干净、更易读的解决方案...

/**
 * method converts {@link InputStream} Object into byte[] array.
 * 
 * @param stream the {@link InputStream} Object.
 * @return the byte[] array representation of received {@link InputStream} Object.
 * @throws IOException if an error occurs.
 */
public static byte[] streamToByteArray(InputStream stream) throws IOException {

    byte[] buffer = new byte[1024];
    ByteArrayOutputStream os = new ByteArrayOutputStream();

    int line = 0;
    // read bytes from stream, and store them in buffer
    while ((line = stream.read(buffer)) != -1) {
        // Writes bytes from byte array (buffer) into output stream.
        os.write(buffer, 0, line);
    }
    stream.close();
    os.flush();
    os.close();
    return os.toByteArray();
}

4
你应该使用try-with-resources。 - Victor Stafusa - BozoNaCadeia
你需要在finally块中完成最后的整理工作,以防出现错误,否则可能会导致内存泄漏。 - MGDavies

1

我尝试编辑@numan的答案,为了解决写入垃圾数据的问题,但是我的编辑被拒绝了。虽然这段代码并不是很出色,但我看不到其他更好的答案。以下是对我来说最有意义的代码:

ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024]; // you can configure the buffer size
int length;

while ((length = in.read(buffer)) != -1) out.write(buffer, 0, length); //copy streams
in.close(); // call this in a finally block

byte[] result = out.toByteArray();

顺便提一下,ByteArrayOutputStream不需要关闭。为了可读性,省略了try/finally结构。

1

请参考 InputStream.available() 的文档:

需要特别注意的是,您不能使用此方法来确定容器的大小,并假设您可以在不需要调整容器大小的情况下读取流的全部内容。这样的调用者可能应该将它们读取的所有内容写入 ByteArrayOutputStream 并将其转换为字节数组。或者,如果您正在从文件中读取,则 File.length 返回文件的当前长度(尽管假定文件的长度不会更改可能是不正确的,从文件中读取本质上存在竞争条件)。


1

如果无法使用DataInputStream,请将其包装在其中,只需使用read命令不断尝试读取,直到返回-1或您要求的整个块为止。

public int readFully(InputStream in, byte[] data) throws IOException {
    int offset = 0;
    int bytesRead;
    boolean read = false;
    while ((bytesRead = in.read(data, offset, data.length - offset)) != -1) {
        read = true;
        offset += bytesRead;
        if (offset >= data.length) {
            break;
        }
    }
    return (read) ? offset : -1;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接