views:

1080

answers:

3

So I have some code that reads a certain amount of bytes from a file and returns the resulting byte array (this is basically used for chunking up files to send over the network as (eventually) base64-encoded ascii text).

It works fine, except that when the last chunk of the file is generated, it isnt a full chunk. Therefore, the resulting byte array isnt full. However, it is a constant size, which means that the file is reassembled there is a whole bunch of extra data (0's maybe) appended to the end.

How can I make it so that the byte[] for the last chunk of the file really only contains the data it needs to? The code looks like this:

 private byte[] readData(File f, int startByte, int chunkSize) throws Exception {
    RandomAccessFile raf = new RandomAccessFile(f, "r");
    raf.seek(startByte);
    byte[] data = new byte[chunkSize];
    raf.read(data);        
    raf.close();
    return data;
}

So if chunkSize is bigger than the remaining bytes in the file, a full sized byte[] gets returned but its only half-full with data.

+2  A: 

RandomAccessFile.read() returns the number of bytes read, so you can do copy the array if needed:

private byte[] readData(File f, int startByte, int chunkSize) throws Exception {
    RandomAccessFile raf = new RandomAccessFile(f, "r");
    raf.seek(startByte);
    byte[] data = new byte[chunkSize];
    int read = raf.read(data);
    raf.close();
    if (read == data.length) return data;
    else
      return Arrays.copyOf(data, read);
}

If you are using Java pre-6, then you need to implement Arrays.copyOf yourself:

byte[] r = new byte[read];
System.arraycopy(data, 0, r, 0, read);
return r;
notnoop
A: 

You'll have to check the return value of RandomAccessFile.read() to determine the number of bytes read. If it's different than the chunkSize, you'll have to copy the array over to a smaller one and return that.

private byte[] readData(File f, int startByte, int chunkSize) throws Exception {
    RandomAccessFile raf = new RandomAccessFile(f, "r");
    raf.seek(startByte);
    byte[] data = new byte[chunkSize];
    int bytesRead = raf.read(data);
    if (bytesRead != chunkSize) {
         byte[] smallerData = new byte[bytesRead];
         System.arraycopy(data, 0, smallerData, 0, bytesRead);
         data = smallerData;
    }
    raf.close();
    return data;
}
Asaph
Using Arrays.copyOf from the post above instead of System.arraycopy (which threw an exception), this worked perfectly! Thanks!
Erin Drummond
Corrected, I had a typo in the comparison, should have used `==` not `=`.
notnoop
@Erin Drummond: What was the Exception thrown by `System.arraycopy()`?
Asaph
it was an IndexOutOfBoundsException iirc
Erin Drummond
@Erin Drummond: I see why that was happening and fixed my answer. The last argument to `System.arraycopy()` should be `bytesRead`, not `chunkSize`. Duh! Sorry about that. Please try with my updated answer and confirm that it's fixed. Thanks :)
Asaph
A: 

You could also use the size of the file to calculate the remaining number of bytes.

private byte[] readData(File f, int startByte, int chunkSize) throws Exception {
    RandomAccessFile raf = new RandomAccessFile(f, "r");
    raf.seek(startByte);
    int size = (int) Math.min(chunkSize, raf.length()-startByte);
    byte[] data = new byte[size];
    raf.read(data);
    // TODO check the value returned by read (throw Exception or loop)
    raf.close();
    return data;
}

This way you don't create an additional Array and do not need the copy. Probably not a big impact.
One important point IMO: check the value returned by read, I think it can be less than the remaining bytes. The javadoc states:

The number of bytes read is, at most, equal to the length of b

Carlos Heuberger