Java中将文件读入字节数组的优雅方法

75

可能是重复问题:
Java中将文件转换为byte[]数组

我想从文件中读取数据并将其解组到Parcel中。 在文档中,对于FileInputStream的read方法不明确,无法读取所有内容。为了实现这一点,我进行如下操作:

FileInputStream filein = context.openFileInput(FILENAME);


int read = 0;
int offset = 0;
int chunk_size = 1024;
int total_size = 0;

ArrayList<byte[]> chunks = new ArrayList<byte[]>();
chunks.add(new byte[chunk_size]);
//first I read data from file chunk by chunk
while ( (read = filein.read(chunks.get(chunks.size()-1), offset, buffer_size)) != -1) {
    total_size+=read;
    if (read == buffer_size) {
         chunks.add(new byte[buffer_size]);
    }
}
int index = 0;

// then I create big buffer        
byte[] rawdata = new byte[total_size];

// then I copy data from every chunk in this buffer
for (byte [] chunk: chunks) {
    for (byte bt : chunk) {
         index += 0;
         rawdata[index] = bt;
         if (index >= total_size) break;
    }
    if (index>= total_size) break;
}

// and clear chunks array
chunks.clear();

// finally I can unmarshall this data to Parcel
Parcel parcel = Parcel.obtain();
parcel.unmarshall(rawdata,0,rawdata.length);

我认为这段代码看起来很丑,我的问题是: 如何以优美的方式将数据从文件读入byte[]? :)

6个回答

143

很久以前:

调用其中任何一个

byte[] org.apache.commons.io.FileUtils.readFileToByteArray(File file)
byte[] org.apache.commons.io.IOUtils.toByteArray(InputStream input) 

来自:

http://commons.apache.org/io/

如果该库对您的Android应用程序来说太大了,您可以只使用来自commons-io库中相关的类。

今天(Java 7+ 或 Android API Level 26+)

幸运的是,我们现在在nio包中有一些方便的方法。例如:

byte[] java.nio.file.Files.readAllBytes(Path path)

这里有Javadoc


10
IOUtils.toByteArray(InputStream) 也可以使用,不过 FileUtils 的方法应该更有效率。 - Stephen C
但是如果文件的内容恰好就是您想要的字节数组的值呢? - Adam Johns
@AdamJohns:这是在Stack Overflow上提出一个*新问题的完美材料。 - Lukas Eder
5
如何在Android中执行相同的操作?byte[] java.nio.file.Files.readAllBytes(Path path)在Android中并不存在...有任何想法吗? - 3lokh
@NikhilGeorge:使用Apache Commons IO或类似的东西,如答案中所提到的...这里还有其他答案,展示了如何使用旧的java.io.InputStream API来完成此操作... - Lukas Eder
显示剩余4条评论

65

这个也可以工作:

import java.io.*;

public class IOUtil {

    public static byte[] readFile(String file) throws IOException {
        return readFile(new File(file));
    }

    public static byte[] readFile(File file) throws IOException {
        // Open file
        RandomAccessFile f = new RandomAccessFile(file, "r");
        try {
            // Get and check length
            long longlength = f.length();
            int length = (int) longlength;
            if (length != longlength)
                throw new IOException("File size >= 2 GB");
            // Read file and return data
            byte[] data = new byte[length];
            f.readFully(data);
            return data;
        } finally {
            f.close();
        }
    }
}

1
@sstn 但是你在所有其他解决方案中都有同样的问题。 - jcsahnwaldt Reinstate Monica
这个被标记为Android,所以我无法想象在Android应用中会有任何问题。 - IcedDante
12
不添加库 +1。 - philipp
太好了!我已经达到了65k方法限制,因为我使用了Guava(它本身就有15k个方法)。让我的代码更小并解决了这个问题。感谢提供一个没有库的解决方案。 - Tobias Reich
它抛出了“java.lang.OutOfMemoryError:无法分配258797515字节的内存,6994314字节空闲,直到OOM为止”,适用于250MB文件。 - Vasile Doe
显示剩余3条评论

40

2
但是guava jar包有1.6MB,这对于一个Android应用来说不是太大了吗? - Paulo Cesar
@PauloCesar:也许吧。我不熟悉Android开发,所以无法对此发表评论。 - Peter Štibraný
2
抱歉,我以为这个问题与Android有关,但现在我意识到它只是纯Java。 - Paulo Cesar
不是,它被标记为Android。但是Guava是Google端点服务的Android客户端所需的,所以我想他们认为它适用于Android。而未使用的代码将在发布构建中被删除。 - Tom
3
@Tom几乎是正确的。默认情况下不会删除,但如果你设置了Proguard,它将删除所有未使用的代码。在本文撰写时,Guava默认添加约2.2MB到APK。但是通过Proguard,只添加了大约250KB。 - yarian

17

这对我来说有效:

File file = ...;
byte[] data = new byte[(int) file.length()];
try {
    new FileInputStream(file).read(data);
} catch (Exception e) {
    e.printStackTrace();
}

5
该方法不能保证读取整个文件。 - pihentagy
2
你说得对,为了确保read()的返回值与file.length()匹配,如果文件不完整则继续读取... - domsom

13

使用 ByteArrayOutputStream。以下是步骤:

  • 获取一个InputStream来读取数据
  • 创建一个ByteArrayOutputStream
  • 将所有的InputStream复制到OutputStream
  • 使用toByteArray()方法从ByteArrayOutputStream中获取byte[]

我有一个OutputStream,如何从中获取ByteArrayOutputStream(因为我想检索byte[])? - Amira

7
请看以下Apache Commons函数:
org.apache.commons.io.FileUtils.readFileToByteArray(File)

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