OutputStream
в этом случае является «материализованным значением» Source
, и оно будет создано только после запуска потока (или «материализации» в текущий поток). Запуск его вне вашего контроля, так как вы передаете Source
в Akka HTTP, и это позже фактически запустит ваш источник.
.mapMaterializedValue(matval -> ...)
обычно используется для преобразования материализованного значения, но так как оно вызывается как В качестве части материализации вы можете использовать это для создания побочных эффектов, таких как отправка матвала в сообщении, как вы уже поняли, в этом нет ничего плохого, даже если это выглядит странно. Важно понимать, что поток не завершит свою материализацию и начнет работать до тех пор, пока лямбда не завершится. Это означает проблемы, если download()
блокирует, а не отбрасывает какую-то работу в другом потоке и немедленно возвращает ее.
Однако существует другое решение: Source.preMaterialize()
, оно материализует источник и дает вам Pair
материализованного значения и нового Source
, который может использоваться для использования уже запущенного источника:
Pair<OutputStream, Source<ByteString, NotUsed>> pair =
StreamConverters.asOutputStream().preMaterialize(system);
OutputStream os = pair.first();
Source<ByteString, NotUsed> source = pair.second();
Обратите внимание, что в вашем коде есть несколько дополнительных вещей, которые следует учитывать, особенно если blobClient.download(os)
вызывайте блоки до тех пор, пока это не будет сделано, и вы вызываете это от актера, в этом случае вы должны убедиться, что ваш актер не морит голодом диспетчер и не останавливать выполнение других акторов в вашем приложении (см. Документы Akka: https://doc.akka.io/docs/akka/current/typed/dispatchers.html#blocking -needs-осторожно-управление ).