Будет ли FileChannel#write всегда записывать весь буфер?

(Это связано с (или, скорее, с "противоположностью") Будет ли FileChannel.read читать меньше байтов, чем указано, если данных достаточно?)

TL; DR:

Будет ли это всегда писать весь буфер...

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

... или необходимо использовать такой цикл:

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

?


В связи с комментарием в другом ответе, я хотел бы спросить, есть ли какие-либо гарантии относительно поведения написания Buffer к FileChannel позвонив FileChannel#write(ByteBuffer),


Просто для справки: в документации сказано

Записывает последовательность байтов в этот канал из данного буфера.

Байты записываются, начиная с текущей позиции файла этого канала, если только канал не находится в режиме добавления, и в этом случае позиция сначала продвигается до конца файла. Файл увеличивается, если необходимо, для размещения записанных байтов, а затем позиция файла обновляется в соответствии с количеством фактически записанных байтов. В противном случае этот метод ведет себя точно так, как указано в интерфейсе WritableByteChannel.

и документация переопределенного метода, WritableByteChannel#write(ByteBuffer) говорит

Записывает последовательность байтов в этот канал из данного буфера.

Предпринята попытка записать до r байтов в канал, где r - это количество байтов, оставшихся в буфере, то есть src.remaining(), на момент вызова этого метода.

Предположим, что записана байтовая последовательность длины n, где 0 <= n <= r. Эта последовательность байтов будет передана из буфера, начиная с индекса p, где p - это позиция буфера в момент вызова этого метода; индекс последнего записанного байта будет p + n - 1. По возвращении позиция буфера будет равна p + n; его предел не изменился.

Если не указано иное, операция записи будет возвращена только после записи всех r запрошенных байтов. Некоторые типы каналов, в зависимости от их состояния, могут записывать только некоторые байты или, возможно, вообще ни одного. Например, канал сокета в неблокирующем режиме не может записать больше байтов, чем свободно в выходном буфере сокета.

Этот метод может быть вызван в любое время. Однако, если другой поток уже инициировал операцию записи на этом канале, то вызов этого метода будет блокироваться до завершения первой операции.

Параметры: src - буфер, из которого должны быть получены байты

Возвращает: количество записанных байтов, возможно, ноль


В вышеупомянутом вопросе о чтении с FileChannel В комментариях обсуждалась точная формулировка и толкование этой документации. Я думаю, что принципиальная разница в документации заключается в том, что для метода read в документации сказано

Операция чтения может не заполнять буфер, и фактически она может вообще не читать никаких байтов.

В отличие от этого, документация метода записи говорит

Если не указано иное, операция записи будет возвращена только после записи всех r запрошенных байтов. Некоторые типы каналов, в зависимости от их состояния, могут записывать только некоторые байты или, возможно, вообще ни одного.

Для меня это означает, что операция записи на FileChannel будет возвращаться только после того, как все байты будут записаны, потому что в документации не указано иное (за исключением утверждения, что возвращаемое значение может быть 0, но это, очевидно, артефакт из переопределенного метода)

Из моих тестов с размером файла до 80 МБ (!) Операция записи всегда записывала весь буфер сразу. Но, конечно, это был всего лишь тест, и его недостаточно для глубокого утверждения. Я пытался отследить вызовы в связанных классах OpenJDK, но они быстро расходятся в различные собственные реализации - и в конце концов, это не должно быть необходимым...

1 ответ

Нет, нет никаких гарантий, что write() исчерпает весь буфер. Документация действительно пытается установить ожидание того, что реализации должны записывать все байты за один раз, но она старается не давать никаких обещаний:

Если не указано иное, операция записи будет возвращена только после записи всех r запрошенных байтов. Некоторые типы каналов, в зависимости от их состояния[1], могут записывать только некоторые байты или, возможно, вообще ни одного.

FileChannel.write() также оставляет место для незавершенных записей:

Записывает последовательность байтов в этот канал из данного буфера.

Байты записываются, начиная с текущей позиции файла этого канала, если только канал не находится в режиме добавления, и в этом случае позиция сначала продвигается до конца файла. Файл увеличивается, если необходимо, для размещения записанных байтов, а затем позиция файла обновляется в соответствии с количеством фактически записанных байтов. В противном случае этот метод ведет себя точно так, как указано в интерфейсе WritableByteChannel.

Таким образом, хотя текст подразумевает, что полная запись является общим случаем, а неполные записи - исключением, он оставляет дверь открытой для альтернативных / будущих реализаций, которые могут (не быть в состоянии) придерживаться этого общего случая.

Как вы указали, это отличие подхода от read(). Я предполагаю, что это потому, что во время консолидации документации все известные и предполагаемые реализации придерживались этого общего случая выполнения полных записей.


[1] Это, вероятно, ссылка на неблокирующие каналы.

Другие вопросы по тегам