Вы можете проверить пример загрузки файла в lagom -Репозиторий рецептов на GitHub.
В основном идея состоит в том, чтобы создать дополнительный роутер Play. После этого мы должны указать Lagom использовать его, как указано в справке. документация (эта функция доступна с версии 1.5.0). Вот как может выглядеть роутер:
class FileUploadRouter(action: DefaultActionBuilder,
parser: PlayBodyParsers,
implicit val exCtx: ExecutionContext) {
private def fileHandler: FilePartHandler[File] = {
case FileInfo(partName, filename, contentType, _) =>
val tempFile = {
val f = new java.io.File("./target/file-upload-data/uploads", UUID.randomUUID().toString).getAbsoluteFile
f.getParentFile.mkdirs()
f
}
val sink: Sink[ByteString, Future[IOResult]] = FileIO.toPath(tempFile.toPath)
val acc: Accumulator[ByteString, IOResult] = Accumulator(sink)
acc.map {
case akka.stream.IOResult(_, _) =>
FilePart(partName, filename, contentType, tempFile)
}
}
val router = Router.from {
case POST(p"/api/files") =>
action(parser.multipartFormData(fileHandler)) { request =>
val files = request.body.files.map(_.ref.getAbsolutePath)
Results.Ok(files.mkString("Uploaded[", ", ", "]"))
}
}
}
А затем мы просто говорим Лагому использовать его.
override lazy val lagomServer =
serverFor[FileUploadService](wire[FileUploadServiceImpl])
.additionalRouter(wire[FileUploadRouter].router)
В качестве альтернативы мы можем использовать класс PlayServiceCall
. Вот простой набросок того, как это сделать, предоставленный Джеймсом Ропером из команды Lightbend:
// The type of the service call is NotUsed because we are handling it out of band
def myServiceCall: ServiceCall[NotUsed, Result] = PlayServiceCall { wrapCall =>
// Create a Play action to handle the request
EssentialAction { requestHeader =>
// Now we create the sink for where we want to stream the request to - eg it could
// go to a file, a database, some other service. The way Play gives you a request
// body is that you need to return a sink from EssentialAction, and when it gets
// that sink, it stream the request body into that sink.
val sink: Sink[ByteString, Future[Done]] = ...
// Play wraps sinks in an abstraction called accumulator, which makes it easy to
// work with the result of handling the sink. An accumulator is like a future, but
// but rather than just being a value that will be available in future, it is a
// value that will be available once you have passed a stream of data into it.
// We wrap the sink in an accumulator here.
val accumulator: Accumulator[ByteString, Done] = Accumulator.forSink(sink)
// Now we have an accumulator, but we need the accumulator to, when it's done,
// produce an HTTP response. Right now, it's just producing akka.Done (or whatever
// your sink materialized to). So we flatMap it, to handle the result.
accumulator.flatMap { done =>
// At this point we create the ServiceCall, the reason we do that here is it means
// we can access the result of the accumulator (in this example, it's just Done so
// not very interesting, but it could be something else).
val wrappedAction = wrapCall(ServiceCall { notUsed =>
// Here is where we can do any of the actual business logic, and generate the
// result that can be returned to Lagom to be serialized like normal
...
})
// Now we invoke the wrapped action, and run it with no body (since we've already
// handled the request body with our sink/accumulator.
wrappedAction(request).run()
}
}
}
Вообще говоря, вероятно, не стоит использовать Lagom для этой цели. Как отмечено в проблеме GitHub в документации PlayServiceCall
:
Многие случаи использования, когда мы возвращаемся к PlayServiceCall, связаны с представлением или использованием, специфичным для HTTP (I18N, загрузка файла, ...), которые указывают: связь службы lagom с уровнем представления или связь службы lagom с транспортом.
Снова цитируя Джеймса Роупера (несколько лет назад):
Так что в настоящее время multipart/form-data не поддерживается в Lagom, по крайней мере, не из коробки. Вы можете перейти на более низкий уровень Play API, чтобы обработать его, но, возможно, было бы лучше обрабатывать его в веб-шлюзе, где любые обрабатываемые файлы загружаются непосредственно в службу хранения, такую как S3, а затем служба Lagom может хранить связанные с ним метаданные.
Вы также можете ознакомиться с обсуждением здесь, где содержится больше информации.
person
arnaudoff
schedule
10.09.2019