Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Will FileChannel#write always write the whole buffer?

(This is related to (or rather the "opposite" of) Would FileChannel.read read less bytes than specified if there's enough data? )

TL;DR :

Will this always write the whole buffer...

ByteBuffer bytes = ...;
fileOutputStream.getChannel().write(bytes);

...or is it necessary to use a loop like this:

ByteBuffer bytes = ...;
while (bytes.remaining() > 0)
{
    fileOutputStream.getChannel().write(bytes);
}

?


Due to a comment in another answer, I'd like to ask whether there are any guarantees regarding the behavior of writing a Buffer to a FileChannel by calling FileChannel#write(ByteBuffer).


Just for reference: The documentation says

Writes a sequence of bytes to this channel from the given buffer.

Bytes are written starting at this channel's current file position unless the channel is in append mode, in which case the position is first advanced to the end of the file. The file is grown, if necessary, to accommodate the written bytes, and then the file position is updated with the number of bytes actually written. Otherwise this method behaves exactly as specified by the WritableByteChannel interface.

and the documentation of the overridden method, WritableByteChannel#write(ByteBuffer) says

Writes a sequence of bytes to this channel from the given buffer.

An attempt is made to write up to r bytes to the channel, where r is the number of bytes remaining in the buffer, that is, src.remaining(), at the moment this method is invoked.

Suppose that a byte sequence of length n is written, where 0 <= n <= r. This byte sequence will be transferred from the buffer starting at index p, where p is the buffer's position at the moment this method is invoked; the index of the last byte written will be p + n - 1. Upon return the buffer's position will be equal to p + n; its limit will not have changed.

Unless otherwise specified, a write operation will return only after writing all of the r requested bytes. Some types of channels, depending upon their state, may write only some of the bytes or possibly none at all. A socket channel in non-blocking mode, for example, cannot write any more bytes than are free in the socket's output buffer.

This method may be invoked at any time. If another thread has already initiated a write operation upon this channel, however, then an invocation of this method will block until the first operation is complete.

Parameters: src - The buffer from which bytes are to be retrieved

Returns: The number of bytes written, possibly zero


In the above mentioned question about reading from a FileChannel, there has been some discussion in the comments about the exact wording and interpretation of this documentation. I think the crucial difference in the documentation is that for the read method, the documentation says

A read operation might not fill the buffer, and in fact it might not read any bytes at all.

In contrast to that, the documentation of the write method says

Unless otherwise specified, a write operation will return only after writing all of the r requested bytes. Some types of channels, depending upon their state, may write only some of the bytes or possibly none at all.

For me, this means that the write operation on a FileChannel will only return after all bytes have been written, because it is not specified otherwise in the documentation (except for the statement that the return value may be 0, but this is obviously an artifact from the overridden method)

From my tests with file sizes of up to 80 MB (!), the write operation always wrote the whole buffer at once. But of course, this was just a test, and is not sufficient for a profound statement. I tried to trace the calls in the related OpenJDK classes, but these quickly diverge into different native implementations - and after all, this should not be necessary...

like image 912
Marco13 Avatar asked Apr 29 '15 13:04

Marco13


People also ask

When to use FileChannel in Java?

In general we can say that FileChannel is a channel that is connected to a file by which you can read data from a file, and write data to a file. Other important characteristic of FileChannel is this that it cannot be set into non-blocking mode and always runs in blocking mode.

What is a FileChannel?

A file channel is a SeekableByteChannel that is connected to a file. It has a current position within its file which can be both queried and modified . The file itself contains a variable-length sequence of bytes that can be read and written and whose current size can be queried.

How does FileChannel work Java?

Reads a sequence of bytes from this channel into the given buffer, starting at the given file position. This method works in the same manner as the read(ByteBuffer) method, except that bytes are read starting at the given file position rather than at the channel's current position.

What is ReadableByteChannel?

Interface ReadableByteChannelA channel that can read bytes. Only one read operation upon a readable channel may be in progress at any given time.


1 Answers

No, there are no guarantees that write() will exhaust the whole buffer. The documentation does try to establish the expectation that implementations should write all bytes in one go, but it takes care to make no promises:

Unless otherwise specified, a write operation will return only after writing all of the r requested bytes. Some types of channels, depending upon their state[1], may write only some of the bytes or possibly none at all.

FileChannel.write() similarly leaves room for incomplete writes:

Writes a sequence of bytes to this channel from the given buffer.

Bytes are written starting at this channel's current file position unless the channel is in append mode, in which case the position is first advanced to the end of the file. The file is grown, if necessary, to accommodate the written bytes, and then the file position is updated with the number of bytes actually written. Otherwise this method behaves exactly as specified by the WritableByteChannel interface.

So, while the text implies the full write to be the general case and incomplete writes to be the exception, it leaves the door open for alternative/future implementations that may not (be able to) adhere to this general case.

As you point out, this is a difference in approach from read(). I imagine this is because, at the time of consolidating the documentation, all known and intended implementations adhered to this general case of performing complete writes.


[1] This is probably a reference to non-blocking channels.

like image 171
JvR Avatar answered Oct 05 '22 12:10

JvR