6

I wrote a file using Java's FileChannel class that uses RandomAccessFiles. I wrote objects at various locations in the file. The objects were of variable sizes but all of the same class. I wrote the objects using the following idea :

ByteArrayOutputStream bos= new ByteArrayOutputStream();
        ObjectOutput out = new ObjectOutputStream(bos);
        out.writeObject(r);
        byte[] recordBytes= bos.toByteArray();

    ByteBuffer rbb= ByteBuffer.wrap(recordBytes);

    while(rbb.hasRemaining()) {
        fileChannel.write(rbb);
    }

Now I want to read from such a file. I dont want to have to specify the number of bytes to read. I want to be able to read the object directly using Object Input Stream. How to achieve this ?

I have to use Random Access Files because I need to write to different positions in file. I am also recording in a separate data structure, the locations where objects have been written.

AnkurVj
  • 7,958
  • 10
  • 43
  • 55

4 Answers4

5

I have to use Random Access Files because I need to write to different positions in file.

No, you don't. You can reposition a FileOutputStream or FileInputStream via its channel.

That would significantly simplify your writing code as well: you wouldn't need to use the buffer or channel, and depending on your needs you could omit the ByteArrayOutputStream as well. However, as you note in a comment, you won't know the size of the object in advance, and the ByteArrayOutputStream is a useful way to verify that you don't overrun your allotted space.

Object obj = // something

FileOutputStream fos = // an initialized stream

ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();

if (bos.size() > MAX_ALLOWED_SIZE)
   throw // or log, or whatever you want to do
else
{
    fos.getChannel().position(writeLocation);
    bos.writeTo(fos);
}

To read the objects, do the following:

FileInputStream fis = // an initialized stream

fis.getChannel().position(offsetOfSerializedObject);
ObjectInputStream iis = new ObjectInputStream(new BufferedInputStream(fis));
Object obj = iis.readObject();

One comment here: I wrapped the FileInputStream in a BufferedInputStream. In this specific case, where the file stream is repositioned before each use, that can provide a performance benefit. Be aware, however, that the buffered stream can read more bytes than are needed, and there are some situations using construct-as-needed object streams where it would be a really bad idea.

parsifal
  • 1,507
  • 8
  • 7
  • 1
    Its a shame I wasted so much time when such a straight forward solution existed. Thankyou very much for this superb solution! – AnkurVj Sep 14 '11 at 14:46
  • I am getting an IOException : write error : Are you sure everything is alright here ? – AnkurVj Sep 14 '11 at 16:09
  • Okay I implemented your solution without the ByteBuffer. If I close the ObjectOutputStream, does the underlying fileoutput stream close as well ? why ? – AnkurVj Sep 14 '11 at 16:17
  • 2
    Yes, `ObjectOutputStream.close()` will propagate. That's why you don't see it in my example. Instead, I call `flush()` to ensure that the content has been written to the stream, and then let the GC dispose of it. – parsifal Sep 14 '11 at 16:25
  • Everything Works ! Now I can say I learned a lot from this exercise – AnkurVj Sep 14 '11 at 16:33
  • Not everything yet :( With ObjectInputStream I am able to read only once, the next time it reports corrupted stream : invalid type code .. any ideas ? – AnkurVj Sep 14 '11 at 16:43
  • That sounds like you're either positioning incorrectly or overwriting previously written data. – parsifal Sep 14 '11 at 16:51
  • When I used BufferedInputStream, I faced this problem, however on removing it and directly using fileinputstream the problem didnt appear. Please help. I reallywant to use BufferedInputStream – AnkurVj Sep 14 '11 at 16:57
  • What exactly is the offset? Is it the number of objects we want to skip or number of bytes? Really confused between the two. Writing some random value inside and trying to read an object gives a StreamCorruptedException. – Dhiraj Gandhi Oct 31 '17 at 07:10
2

Why doesn't seek work for you? I believe you need to seek() to correct locations and then just read objects using your object stream. Also, if you store the correct locations of serialized objects, why don't you store their sizes? In this case you may apply ObjectInputStream against bytes you read from file.

Andrey Agibalov
  • 7,624
  • 8
  • 66
  • 111
1

The simplest solution that comes to mind is to write out the length of the array before writing out the array itself:

while(rbb.hasRemaining()) {
        fileChannel.writeLong(recordBytes.length);
        fileChannel.write(rbb);
    }

When reading the object, you first read the length. This'll tell you how many further bytes to read to get your object. Similarly to what you are already doing on the writing side, you could read the data into a byte[] and then use ByteArrayInputputStream and ObjectInputStream.

NPE
  • 486,780
  • 108
  • 951
  • 1,012
  • Yes, this is a possible solution to my problem, however I was wondering if this could be done _without_ the explicit size writing. I mean, when i use ObjectInputStream on normal Files, I never have to specify the size of the object I want to read ! – AnkurVj Sep 13 '11 at 07:18
  • There is no writeLong in fileChannel, so I'd have to do that too with more Lines of Code – AnkurVj Sep 13 '11 at 20:20
  • Furthermore I don't know of a trivial way to find the size of an object in Java – AnkurVj Sep 13 '11 at 20:24
1

You could use a FileInputStream constructed on the RandomAccesFile's FileDescriptor object, like so:

FileDescriptor f = raf.getFD();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f));

Assuming that the RandomAccessFile is called raf.

bob_twinkles
  • 252
  • 2
  • 9
  • My random access file is encapsulated inside the FileChannel. How do obtain the Random Access File object from this channel object ? – AnkurVj Sep 13 '11 at 07:16
  • I do not belive you can - here is a link to the doc though, just in case I missed something. - http://download.oracle.com/javase/6/docs/api/java/nio/channels/FileChannel.html. If possible, it would be best if you just passed the `RandomAccessFile` to the method writing the objects. – bob_twinkles Sep 13 '11 at 23:38
  • Supposed I have opened the raf with mode "r". If I the close the FileInputStream, will the raf also be closed? –  Nov 03 '11 at 19:24
  • Maybe. The way I think the VM implements the IO system, it should, but no garentees. – bob_twinkles Dec 11 '11 at 22:42