Lagom中的多部分表单数据



我想有一个服务,它接收一个项目对象,该对象包含;名称,描述,价格和图片

  1. 其他属性是字符串,可以很容易地作为Json对象发送,但对于包括图片什么是最好的解决方案?
  2. 如果多部分表单数据是最好的解决方案,它是如何在Lagom处理?

你可能想要查看GitHub上的lagom-recipes存储库中的文件上传示例。

基本上,这个想法是创建一个额外的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[", ", ", "]"))
      }
  }
}

然后,我们告诉Lagom使用它

  override lazy val lagomServer =
    serverFor[FileUploadService](wire[FileUploadServiceImpl])
      .additionalRouter(wire[FileUploadRouter].router)

或者,我们可以使用PlayServiceCall类。以下是由Lightbend团队的James Roper提供的简单草图:

// 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服务与传输的耦合。

再次引用詹姆斯·罗珀(几年前)的话:

所以目前,Lagom不支持multipart/form-data,至少不是开箱即装的。你可以使用较低级别的Play API来处理它,但也许在web网关中处理它会更好,其中处理的任何文件都直接上传到S3等存储服务,然后Lagom服务可能会存储与之相关的元数据。

您也可以查看此处的讨论,其中提供了更多的见解。

相关内容

  • 没有找到相关文章

最新更新