Как я могу обработать файл, загруженный через форму HTML в Spray / Scala / Java? - PullRequest
8 голосов
/ 21 сентября 2011

У меня есть приложение, написанное с использованием Spray , и у меня есть страница с элементом формы <input type="file" name="foo">, который получает POSTed значение /fileUpload.

У меня настроен маршрут Spray для прослушивания пути /fileUpload с использованием этого кода:

path("fileUpload") {
  get { ctx => {
    val request: HttpRequest = ctx.request
    //Process the file, somehow with request?
    ctx.complete("File Uploaded")
  }}
}

Я не могу понять, как получить тело POST и получить дескриптор файла, и я не могу найти никаких примеров в Интернете.

Должно быть возможно получить файл и обработать его с помощью Spray или даже с помощью простого Scala или Java, но я не знаю, как это сделать.

Кто-нибудь может помочь?
Спасибо!

Ответы [ 4 ]

9 голосов
/ 16 сентября 2012

Это возможно с помощью Spray, хотя я не проверял, работает ли потоковая передача должным образом.Я немного поиграл, и все заработало:

  post {
    content(as[MultipartFormData]) {
      def extractOriginalText(formData: MultipartFormData): String = {
        formData.parts.mapValues { (bodyPart) =>
          bodyPart.content.map{
            (content) => new String(content.buffer)
          }
        }.values.flatten.foldLeft("")(_ + _)
      }
      formData =>
        _.complete(
          extractOriginalText(formData)
        );
    }

Если вы загружаете простой текстовый файл в службу, в которой есть этот код, он откашливает оригинальный текст.У меня это работает вместе с загрузкой AJAX;он также должен работать со старомодной формой загрузки файлов.

Мне кажется, что должен быть более простой способ сделать это, особенно глубокое вложение контента довольно неуклюже.Дайте мне знать, если найдете упрощение.

ОБНОВЛЕНИЕ (thx akauppi):

entity(as[MultipartFormData]) { formData => 
  complete( formData.fields.map { _.entity.asString }.flatten.foldLeft("")(_ + _) ) 
}
4 голосов
/ 01 августа 2014

Я использовал код ниже. Это было не слишком сложно, но где-то об этом действительно должен был быть образец Spray.

multipart/form-data формы всегда должны использоваться (вместо традиционных application/x-www-form-urlencoded), если задействованы двоичные загрузки. Подробнее здесь .

Мои требования были:

  • необходимо загрузить двоичные файлы достаточно большого размера
  • хотел бы иметь метаданные в качестве полей (не встроенных в URL или имя файла загрузки)

Некоторые вопросы:

  • Является ли способ управления ошибками «лучшим» способом?

Суть разработки REST API заключается в том, чтобы относиться к клиенту как к «человеку» (в отладке мы), давая значимые сообщения об ошибках в случае, если с сообщением что-то не так.

post {
  // Note: We cannot use a regular 'return' to provide a routing mid-way. The last item matters, but we can
  // have a 'var' which collects the correct success / error info into it. It's a write-once variable.
  //
  var ret: Option[Route] = None

  // Multipart form
  //
  // To exercise this:
  //  $ curl -i -F "file=@filename.bin" -F "computer=MYPC" http://localhost:8080/your/route; echo
  //
  entity(as[MultipartFormData]) { formData =>
    val file = formData.get("file")
      // e.g. Some(
      //        BodyPart( HttpEntity( application/octet-stream, ...binary data...,
      //                    List(Content-Type: application/octet-stream, Content-Disposition: form-data; name=file; filename=<string>)))
    log.debug( s".file: $file")

    val computer = formData.get("computer")
    // e.g. Some( BodyPart( HttpEntity(text/plain; charset=UTF-8,MYPC), List(Content-Disposition: form-data; name=computer)))
    log.debug( s"computer: $computer" )

    // Note: The types are mentioned below simply to make code comprehension easier. Scala could deduce them.
    //
    for( file_bodypart: BodyPart <- file;
        computer_bodypart: BodyPart <- computer ) {
      // BodyPart: http://spray.io/documentation/1.1-SNAPSHOT/api/index.html#spray.http.BodyPart

      val file_entity: HttpEntity = file_bodypart.entity
        //
        // HttpEntity: http://spray.io/documentation/1.1-SNAPSHOT/api/index.html#spray.http.HttpEntity
        //
        // HttpData: http://spray.io/documentation/1.1-SNAPSHOT/api/index.html#spray.http.HttpData

      log.debug( s"File entity length: ${file_entity.data.length}" )

      val file_bin= file_entity.data.toByteArray
      log.debug( s"File bin length: ${file_bin.length}" )

      val computer_name = computer_bodypart.entity.asString    //note: could give encoding as parameter
      log.debug( s"Computer name: $computer_name" )

      // We have the data here, pass it on to an actor and return OK
      //
      ...left out, application specific...

      ret = Some(complete("Got the file, thanks.")) // the string doesn't actually matter, we're just being polite
    }

    ret.getOrElse(
      complete( BadRequest, "Missing fields, expecting file=<binary>, computer=<string>" )
    )
  }
}
2 голосов
/ 22 сентября 2011

Хорошо, после попытки написать Spray Unmarshaller для многокомпонентных данных формы, я решил просто написать scala HttpServlet, который получит отправку формы, и использовал библиотеку Apache FileUpload для обработки запроса:

class FileUploadServlet extends HttpServlet {

  override def doPost(request: HttpServletRequest, response: HttpServletResponse) {
    val contentType = request.getContentType
    val boundary = contentType.substring(contentType.indexOf("boundary=")+9)
    val multipartStream = new MultipartStream(request.getInputStream, boundary)

    // Do work with the multipart stream
  }

}
1 голос
/ 13 октября 2015

Чтобы получить опубликованный (возможно, двоичный) файл и временно вставить его куда-нибудь, я использовал это:

  post {
    entity(as[MultipartFormData]) {
      formData => {
        val ftmp = File.createTempFile("upload", ".tmp", new File("/tmp"))
        val output = new FileOutputStream(ftmp)
        formData.fields.foreach(f => output.write(f.entity.data.toByteArray ) )
        output.close()
        complete("done, file in: " + ftmp.getName())
      }
    }
  }
...