#file #scala #playframework
#файл #scala #playframework
Вопрос:
Мне нужно предоставить некоторый контент из действия в виде файла: по сути, я создаю содержимое CSV «на лету» и отправляю его клиенту.
Я не могу сделать это с помощью sendFile, поскольку файл на самом деле не существует; Я пытался использовать фрагментированную передачу, но получаю очень медленный ответ (на локальном хосте я получил файл со скоростью около 100 КБ / с, что, на мой взгляд, действительно странно).
Есть ли у меня способ установить тип содержимого и написать ответ «построчно», без необходимости указывать длину содержимого «априори»?
Комментарии:
1. Это для Java или Scala?
2. Scala! Извините, я забыл написать это выше…
Ответ №1:
Вот один из способов, использующий простой предопределен Enumerator
, который будет выдавать ответ из байтов, записанных в OutputStream
:
def csv = Action {
val enumerator = Enumerator.outputStream { out =>
out.write(...)
// Keep writing to the Enumerator
out.close()
}
Ok.chunked(enumerator.andThen(Enumerator.eof)).withHeaders(
"Content-Type" -> "text/csv",
"Content-Disposition" -> s"attachment; filename=test.csv"
)
}
Это достаточно просто для относительно небольших файлов (или если процесс создания файла медленный по своей природе), однако обратите внимание, что из документации это не имеет обратного давления, чтение большого файла в OutputStream
может быстро заполнить память, если клиент не может загрузить его достаточно быстро.
Обновить:
После еще одного тестирования кажется, что размер Byte
массивов, которые вы записываете OutputStream
, имеет огромное значение в пропускной способности.
Используя этот пример потока:
val s = Stream.continually(0.toByte)
Запись в виде фрагментов размером 1 КБ в OutputStream
подобное это привело к пропускной способности 6 МБ / с.:
(0 until 1024*1024).foreach{i =>
out.write(s.take(1024).toArray)
}
Однако, если я записываю только 10 байт за раз, пропускная способность замедляется до менее 100 КБ / с. Итак, мое предложение использовать этот метод для записи CSV в фрагментированной форме заключалось бы в том, чтобы записывать несколько строк за раз OutputStream
, а не по одной строке за раз.
Комментарии:
1. Я думаю, что я уже пробовал это, и по какой-то причине файл загружался довольно медленно… Я попробую сделать это как можно скорее и добавлю больше комментариев.
2. Вы уверены, что не просто медленно генерируете файл? Я смог очень быстро обслуживать большие файлы, подобные этому.
3. Хорошо, я попробовал, и фрагментированный ответ, похоже, является самой медленной частью: я регистрирую момент непосредственно перед закрытием выходного потока, и это происходит очень быстро, но затем для воспроизведения требуется 10 секунд, чтобы отправить результирующие данные клиенту (1 МБ при 100 КБ / с). Есть идеи?
4. @AlessandroSivieri Я обновил свой ответ, включив в него некоторые новые материалы, которые, вероятно, решат вашу проблему.