How can we write a byte array to a file (and read it back from that file) in Java?
Yes, we all know there are already lots of questions like that, but they get very messy and subjective due to the fact that there are so many ways to accomplish this task.
So let's reduce the scope of the question:
Domain:
- Android / Java
What we want:
- Fast (as possible)
- Bug-free (in a rigidly meticulous way)
What we are not doing:
- Third-party libraries
- Any libraries that require Android API later than 23 (Marshmallow)
(So, that rules out Apache Commons, Google Guava, Java.nio, and leaves us with good ol' Java.io as far as I can tell.)
What we need:
- Byte array is always exactly the same (content and size) after going through the write-then-read process
- Write method only requires two arguments: File file, and byte[] data
- Read method returns a byte[] and only requires one argument: File file
NEITHER read OR write methods are responsible for:
- Thread-safety (file will not be accessed by more than one process at once)
- File being null
- File pointing to non-existent location
- Lack of permissions at the file location
Write Method is NOT responsible for:
- Byte array being too large
- Byte array being null
- Dealing with any "index," "length," or "append" arguments/capabilities
So... we're sort of in search of the definitive bullet-proof code that people in the future can assume is safe to use because your answer has lots of up-votes and there are no comments that say, "That might crash if..."
This is what I have so far:
Write Bytes To File:
private void writeBytesToFile(final File file, final byte[] data) {
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write(data);
fos.close();
} catch (Exception e) {
Log.i("XXX", "BUG: " e);
}
}
Read Bytes From File:
private byte[] readBytesFromFile(final File file) {
RandomAccessFile raf;
byte[] bytesToReturn = new byte[(int) file.length()];
try {
raf = new RandomAccessFile(file, "r");
raf.readFully(bytesToReturn);
} catch (Exception e) {
Log.i("XXX", "BUG: " e);
}
return bytesToReturn;
}
From what I've read, the possible Exceptions are:
FileNotFoundException : Am I correct that this should not happen as long as the file path being supplied was derived using Android's own internal tools and/or if the app was tested properly?
IOException : I don't really know what could cause this... but I'm assuming that there's no way around it if it does.
So with that in mind... can these methods be improved or replaced, and if so, with what?
CodePudding user response:
The simplest, use java's Files.readAllBytes
Reads all the bytes from a file. The method ensures that the file is closed when all bytes have been read or an I/O error, or other runtime exception, is thrown. Note that this method is intended for simple cases where it is convenient to read all bytes into a byte array. It is not intended for reading in large files.
Writes bytes to a file. The options parameter specifies how the the file is created or opened. If no options are present then this method works as if the CREATE, TRUNCATE_EXISTING, and WRITE options are present. In other words, it opens the file for writing, creating the file if it doesn't exist, or initially truncating an existing regular-file to a size of 0. All bytes in the byte array are written to the file. The method ensures that the file is closed when all bytes have been written (or an I/O error or other runtime exception is thrown). If an I/O error occurs then it may do so after the file has created or truncated, or after some bytes have been written to the file. Usage example: By default the method creates a new file or overwrites an existing file. Suppose you instead want to append bytes to an existing file:
Path path = ... byte[] bytes = ... Files.write(path, bytes, StandardOpenOption.APPEND);
CodePudding user response:
I suggest using a BufferedInputStream
and a BufferedOutputStream
with appropriate buffer size.
Write to file
public static void write(final File file, final byte[] bytes) {
try (final BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file), 64 * 1024)) {
bos.write(bytes);
} catch (final Exception ex) {
Log.w("XXX", "Cannot write to file: " ex);
}
}
Read from file
public static byte[] read(final File file) {
byte[] bytes = new byte[(int) file.length()]; // can only read up to 2GB
try (final BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file), 64 * 1024)) {
bis.read(bytes);
} catch (final Exception ex) {
Log.w("XXX", "Cannot read from file: " ex);
}
return bytes;
}
The buffer size is importatant for performance. I used 64K buffer, but you can play around with it and choose a buffer size that fits your needs. But it should be at least 8K and best to be multiple of file system block size.
Also be careful when catching general Exception
, as it can also catch exceptions that might be indication of a larger problem that just writing/reading a file