tags:

views:

3785

answers:

6

What is the best way to convert a java.io.File to a byte[]?

A: 
// 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
            throw new IOException("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;

        try {
            while (offset < bytes.length
                   && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
                offset += numRead;
            }
        } catch (IOException e) {
            throw e;
        } finally {
            is.close();
        }

        // Ensure all the bytes have been read in
        if (offset < bytes.length) {
            throw new IOException("Could not completely read file "+file.getName());
        }
        return bytes;
    }
Cuga
That doesn't close the stream, if an exception happens.
Esko Luontola
Agreed. Always always always close streams in a finally!
Neil Coffey
Updated with better exception handling.
Cuga
Also, put numRead inside the loop. Declare variables in the smallest valid scope you can. Putting it outside the while loop is only necessary to enable that complicated "while" test; it would be better to do the test for EOF inside the loop (and throw an EOFException if it occurs).
erickson
The exception handling now is just odd.
Tom Hawtin - tackline
+9  A: 

It depends of what best means for you. Productivity wise, don't reeinvent the wheel and use Jakarta commons IOUtils.toByteArray(InputStream input)

svachon
given the examples below, correctness-wise as well ...
kdgregory
Or if you're a Guava consumer, ByteStreams.toByteArray(InputStream).http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/io/ByteStreams.html#toByteArray(java.io.InputStream)
Brian Harris
+3  A: 

You can use the NIO api as well to do it. I could do this with this code as long as the total file size (in bytes) would fit in an int.

    File f = new File("c:\\wscp.script");
    FileInputStream fin = null;
    FileChannel ch = null;
    try {
        fin = new FileInputStream(f);
        ch = fin.getChannel();
        int size = (int) ch.size();
        MappedByteBuffer buf = ch.map(MapMode.READ_ONLY, 0, size);
        byte[] bytes = new byte[size];
        buf.get(bytes);

    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } finally {
        try {
            if (fin != null) {
                fin.close();
            }
            if (ch != null) {
                ch.close();
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

I think its very fast since its using MappedByteBuffer.

Amit
I've never used FileChannel's before, but that's a good idea...
R. Bemrose
there is absolutely no need to use memory mapping if you are only going to read the file once, and it will end up using twice as much memory as using a normal FileInputStream.
james
Unfortunately MappedByteBuffer isn't automatically released.
Tom Hawtin - tackline
awesome, the new example includes printStackTrace, classic broken exception handling.
james
I agree.. Its the default stuff that eclipse puts in. I think I should rethrow the exception !
Amit
+7  A: 

As someone said, Apache Commons File Utils might have what you are looking for

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

Basically you have to read it in memory. Open the file, allocate the array, an read the contents from the file into that array.

The simplest way is something similar to this:

public byte[] read(File file) throws IOException, FileTooBigException {

    if ( file.length() > MAX_FILE_SIZE ) {
        throw new FileTooBigException(file);
    }

    try {
        byte []buffer = new byte[4096];
        ByteArrayOutputStream ous = new ByteArrayOutputStream();
        InputStream ios = new FileInputStream(file);
        int read = 0;
        while ( (read = ios.read(buffer)) != -1 ) {
            ous.write(buffer, 0, read);
        }
    } finally { 
        try {
             if ( ous != null ) 
                 ous.close();
        } catch ( IOEXception e) {
        }

        try {
             if ( ios != null ) 
                  ios.close();
        } catch ( IOException e) {
        }
    }
    return ous.toByteArray();
}

This has some unnecessary copying of the file content (actually the data is copied three times: from file to buffer, from buffer to BAOS, from BAOS to the actual resuting array).

You also need to make sure you read in memory only files up to a certain size (this is usually application dependent) :-).

You also need to treat the IOException outside the function.

Another way is this:

public byte[] read(File file) throws IOException, FileTooBigException {

    if ( file.length() > MAX_FILE_SIZE ) {
        throw new FileTooBigException(file);
    }


    byte []buffer = new byte[(int) file.length()];
    try {
        InputStream ios = new FileInputStream(file);
        if ( ios.read(buffer) == -1 ) {
            throw new IOException("EOF reached while trying to read the whole file");
        }        
    } finally { 
        try {
             if ( ios != null ) 
                  ios.close();
        } catch ( IOException e) {
        }
    }

    return buffer;
}

This has no unnecessary copying.

FileTooBigException is a custom application exception. The MAX_FILE_SIZE constant is an application parameters.

For big files you should probably think a stream processing algorithm or use memory mapping (see java.nio).

Toader Mihai Claudiu
A: 

Let me add another solution without using third-party libraries. It re-uses an exception handling pattern that was proposed by Scott (link). And I moved the ugly part into a separate message (I would hide in some FileUtils class ;) )

public void someMethod() {
    final byte[] buffer = read(new File("test.txt"));
}

private byte[] read(final File file) {
    if (file.isDirectory())
     throw new RuntimeException("Unsupported operation, file "
       + file.getAbsolutePath() + " is a directory");
    if (file.length() > Integer.MAX_VALUE)
     throw new RuntimeException("Unsupported operation, file "
       + file.getAbsolutePath() + " is too big");

    Throwable pending = null;
    FileInputStream in = null;
    final byte buffer[] = new byte[(int) file.length()];
    try {
     in = new FileInputStream(file);
     in.read(buffer);
    } catch (Exception e) {
     pending = new RuntimeException("Exception occured on reading file "
       + file.getAbsolutePath(), e);
    } finally {
     if (in != null) {
      try {
       in.close();
      } catch (Exception e) {
       if (pending == null) {
        pending = new RuntimeException(
         "Exception occured on closing file" 
                             + file.getAbsolutePath(), e);
       }
      }
     }
     if (pending != null) {
      throw new RuntimeException(pending);
     }
    }
    return buffer;
}
Andreas_D