在Java中将二进制输入流读取到单个字节数组中

51

文档指出不应该使用available()方法来确定InputStream的大小。那我怎样才能将InputStream的整个内容读入一个字节数组中呢?

InputStream in; //assuming already present
byte[] data = new byte[in.available()];
in.read(data);//now data is filled with the whole content of the InputStream

我可以多次将数据读入到一个固定大小的缓冲区中,但是这样我将不得不将所读取的数据组合成一个单一的字节数组,这对我来说是一个问题。

6个回答

65

我认为最简单的方法是使用Guava及其ByteStreams类:

byte[] bytes = ByteStreams.toByteArray(in);

或者对于一个文件:

byte[] bytes = Files.toByteArray(file);

或者(如果您不想使用Guava),您可以创建一个 ByteArrayOutputStream,重复读取到一个字节数组并写入到 ByteArrayOutputStream 中(让其处理调整大小),然后调用ByteArrayOutputStream.toByteArray()

请注意,此方法适用于无论您是否能够确定输入的长度 - 假设您有足够的内存,当然。


谢谢。现在已经让它工作了。我想我将不得不分块读取,然后将结果字节数组合并成一个单一的数组。 - Qiang Li
1
@Qiang:这基本上是我提出的所有解决方案所做的事情——你不需要为此做太多工作。 - Jon Skeet

63

请记住,这里的答案假定文件长度小于或等于Integer.MAX_VALUE(2147483647)。

如果您从文件中读取,可以这样做:

    File file = new File("myFile");
    byte[] fileData = new byte[(int) file.length()];
    DataInputStream dis = new DataInputStream(new FileInputStream(file));
    dis.readFully(fileData);
    dis.close();

更新(2014年5月31日):

Java 7在java.nio.file包中增加了一些新功能,可以用于使此示例更短。请参见java.nio.file.Files类中的readAllBytes()方法。以下是一个简短的示例:

import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;

// ...
        Path p = FileSystems.getDefault().getPath("", "myFile");
        byte [] fileData = Files.readAllBytes(p);

Api level 26(8.0.0,奥利奥)开始,Android已经支持此功能。


很遗憾,我不是从文件中读取。我正在从ZipEntry中读取。有没有办法获得类似于这里的文件长度? - Qiang Li

10

您可以使用 Apache commons-io 来完成此任务:

请参考该方法

public static byte[] readFileToByteArray(File file) throws IOException

更新:

Java 7 的方式:

byte[] bytes = Files.readAllBytes(Paths.get(filename));

如果它是一个文本文件,你想将其转换为字符串(根据需要更改编码):

StandardCharsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString()

6
您可以分块读取它(byte buffer[] = new byte[2048]),并将这些块写入 ByteArrayOutputStream 中。从 ByteArrayOutputStream 中,您可以检索内容作为 byte[],而不需要预先确定其大小。

5
我认为需要指定缓冲区长度,因为内存是有限的,你可能会用完它。
例如:
InputStream in = new FileInputStream(strFileName);
    long length = fileFileName.length();

    if (length > Integer.MAX_VALUE) {
        throw new IOException("File is too large!");
    }

    byte[] bytes = new byte[(int) length];

    int offset = 0;
    int numRead = 0;

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

    if (offset < bytes.length) {
        throw new IOException("Could not completely read file " + fileFileName.getName());
    }

    in.close();

3

数组索引最大值为Integer.MAX_INT,约为2Gb(2^31 / 2 147 483 647)。 您的输入流可能比2Gb还要大,因此您需要分块处理数据,抱歉。

        InputStream is;
        final byte[] buffer = new byte[512 * 1024 * 1024]; // 512Mb
        while(true) {
            final int read = is.read(buffer);
            if ( read < 0 ) {
                break;
            }
            // do processing 
        }

1
如果没有最大整数信息,我会将其减1 - 你不应该编写不必要复杂的代码 - while(true) 真是太过了 - 请编辑:int read; while(read = is.read(buffer) > 0 ) - Mr_and_Mrs_D

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