составные данные формы в Lagom

Я хочу иметь службу, которая получает объект элемента, который содержит объект; название, описание, цена и фото.

  1. другие атрибуты представляют собой строки, которые легко можно отправить как объект Json, но для включения изображения какое решение является лучшим?
  2. если multipart formdata - лучшее решение, как оно обрабатывается в Lagom?

person Amir-Mousavi    schedule 31.10.2016    source источник


Ответы (1)


Вы можете проверить пример загрузки файла в 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
comment
Хотя эта ссылка может ответить на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если связанная страница изменится. – Из обзора - person Krzysztof Atłasik; 10.09.2019
comment
@KrzysztofAtłasik, извините, этого не знал. Попытался указать ему правильное направление с дополнительным объяснением сейчас. - person arnaudoff; 10.09.2019
comment
Пожалуйста, просто добавьте пример, используя предоставленные вам источники, и вы обязательно получите принятый ответ. +1 от меня - person Krzysztof Atłasik; 10.09.2019
comment
Готово, я просто надеюсь помочь еще нескольким людям, у которых такая же проблема, как у меня, независимо от того, принята она или нет. Ваше здоровье. - person arnaudoff; 10.09.2019
comment
Конечно, я просто хотел объяснить, почему у некоторых из вас были минусы. В любом случае отличная работа! - person Krzysztof Atłasik; 10.09.2019