Попробуйте:
channel2.write(mappedByteBuffer.duplicate());
channel.write(mappedByteBuffer);
Чтобы понять NIO Buffers, нужно помнить о его основных свойствах:
- базовое хранилище данных (обычно это обычный байтовый массив)., но могут быть и другие вещи, такие как отображаемая в памяти область файла);
- начало и емкость в пределах этого базового пространства;
- ваш текущий позиция в буфере;и
- предел буфера.
Все операции с буфером, предоставляемые NIO, задокументированы с точки зрения того, как операция влияет на эти свойства.Например, документация WritableByteChannel.write()
сообщает нам, что:
- От 0 до
src.remaining()
(включительно) байтов будет записано в канал;и - Если записано
count
байт, позиция байтового буфера будет увеличена на count
при возврате write()
.
Итак, глядя на вашисходный код:
channel2.write(mappedByteBuffer); // Received correctly
channel.write(mappedByteBuffer); // Never received
Если при первой записи все оставшиеся значения mappedByteBuffer
записываются в channel2
, то после этого оператора mappedByteBuffer.remaining()
будет равен нулю, поэтому при записи в channel
не будут записываться никакие байты.вообще.
Следовательно, мое предложение выше использовать ByteBuffer.duplicate()
при первой записи.Этот метод возвращает новый объект ByteBuffer
, который:
- разделяет базовое хранилище исходного буфера (поэтому вы не создаете ненужную копию в памяти фактических байтов, которые хотите записать дважды);но
- имеет свои собственные позиции (и оставшиеся) значения, поэтому, когда
channel2.write()
корректирует эту (дублирующую) позицию ByteBuffer, он не изменит позицию в исходном буфере,
поэтому channel.write()
все равно получит предполагаемый диапазон байтов.
В качестве альтернативы вы также можете написать:
mappedByteBuffer.mark(); // store the current position
channel2.write(mappedByteBuffer);
mappedByteBuffer.reset(); // move position to the previously marked position
channel.write(mappedByteBuffer);
Я также склонен согласиться с точкой зрения EJPчто вы, вероятно, не используете MappedByteBuffer
здесь наилучшим образом.Вы можете упростить свой цикл копирования до:
ByteBuffer buffer = ByteBuffer.allocate(Resource.MEMORY_ALLOC_SIZE);
while (fic.read(buffer) >= 0) {
buffer.flip();
channel2.write(buffer.duplicate());
channel.write(buffer);
}
Здесь метод read()
увеличивает позицию на количество байтов, считанных с канала, а затем метод flip()
устанавливает предел в эту позицию и обратно в 0, что означает, что только что прочитанные вами байты находятся в оставшемся диапазоне, который будет использовать write()
.
Однако вы заметите, что цикл EJP немного большесложнее, чем это.Это связано с тем, что операции записи на каналах не обязательно записывают каждый оставшийся байт.(В документации write()
приведен пример сетевого сокета, открытого в неблокирующем режиме.) Однако этот пример кода (и аналогичный пример в документации ByteBuffer.compact()
) основан на том факте, что вы пишете только водин канал;когда вы пишете на два разных канала, вы должны учитывать тот факт, что два канала могут принимать разное количество байтов.Итак:
ByteBuffer buffer = ByteBuffer.allocate(Resource.MEMORY_ALLOC_SIZE);
while (fic.read(buffer) >= 0) {
buffer.flip();
buffer.mark();
while (buffer.hasRemaining()) {
channel2.write(buffer);
}
buffer.reset():
while (buffer.hasRemaining()) {
channel.write(buffer);
}
buffer.clear();
}