Как использовать NIO для записи InputStream в файл?

Я использую следующий способ записи InputStream в File:

private void writeToFile(InputStream stream) throws IOException {
    String filePath = "C:\\Test.jpg";
    FileChannel outChannel = new FileOutputStream(filePath).getChannel();       
    ReadableByteChannel inChannel = Channels.newChannel(stream);
    ByteBuffer buffer = ByteBuffer.allocate(1024);

    while(true) {
        if(inChannel.read(buffer) == -1) {
            break;
        }

        buffer.flip();
        outChannel.write(buffer);
        buffer.clear();
    }

    inChannel.close();
    outChannel.close();
}

Мне было интересно, правильно ли это использовать NIO. Я прочитал метод FileChannel.transferFrom, который принимает три параметра:

  • ReadableByteChannel src
  • длинная позиция
  • длинный счет

В моем случае у меня есть только src, у меня нет position и count, можно ли каким-либо образом использовать этот метод для создания файла?

Также для изображения есть ли лучший способ создать изображение только с InputStream и NIO?

Любая информация была бы очень полезной для меня. Здесь есть похожие вопросы, в SO, но я не могу найти какое-либо конкретное решение, которое подходит для моего случая.

Ответ 1

Нет, это не правильно. Вы рискуете потерять данные. Канонический цикл копирования NIO выглядит следующим образом:

while (in.read(buffer) >= 0 || buffer.position() > 0)
{
  buffer.flip();
  out.write(buffer);
  buffer.compact();
}

Обратите внимание на измененные условия цикла, которые обеспечивают очистку выхода в EOS и использование compact() вместо clear(),, которое заботится о возможности коротких записей.

Аналогично, канонический цикл transferTo()/transferFrom() выглядит следующим образом:

long offset = 0;
long quantum = 1024*1024; // or however much you want to transfer at a time
long count;
while ((count = out.transferFrom(in, offset, quantum)) > 0)
{
    offset += count;
}

Он должен быть вызван в цикле, так как не гарантируется передача всего кванта.

Ответ 2

Я бы использовал Files.copy

Files.copy(is, Paths.get(filePath));

как для вашей версии

  • ByteBuffer.allocateDirect работает быстрее - Java приложит максимум усилий для выполнения собственных операций ввода-вывода напрямую.

  • Закрытие недостоверно, если первое не удается, второй никогда не будет выполнен. Вместо этого используйте try-with-resources, а также каналы AutoCloseable.