Загрузка файлов с помощью Akka HTTP

Я пытаюсь реализовать функциональность загрузки файлов в моем приложении с помощью Akka HTTP. Я использую akka-stream версия 2.4.4.

вот код (изменен с akka-doc)

path("fileupload") {
    post {
      extractRequestContext {
        ctx => {
          implicit val materializer = ctx.materializer
          implicit val ec = ctx.executionContext
          fileUpload("fileUpload") {
            case (metadata, byteSource) =>
              val location = FileUtil.getUploadPath(metadata)
              val updatedFileName = metadata.fileName.replaceAll(" ", "").replaceAll(""", "")
              val uniqFileName = uniqueFileId.concat(updatedFileName)
              val fullPath = location + File.separator + uniqFileName
              val writer = new FileOutputStream(fullPath)
              val bufferedWriter = new BufferedOutputStream(writer)

              val result = byteSource.map(s => {
                bufferedWriter.write(s.toArray)
              }).runWith(Sink.ignore)

              val result1 = byteSource.runWith(Sink.foreach(s=>bufferedWriter.write(s.toArray)))
              Await.result(result1, 5.seconds)
              bufferedWriter.flush()
              bufferedWriter.close()
              complete(uniqFileName)
            /*onSuccess(result) { x =>
              bufferedWriter.flush()
              bufferedWriter.close()
              complete("hello world")
            }*/
          }
        }
      }
    }
  }

этот код работает нормально и загружает файл по заданному пути. Я генерирую новые имена файлов, добавляя UUID, чтобы убедиться, что имена файлов уникальны. Поэтому мне нужно вернуть новое имя файла вызывающему. Однако, этот метод не возвращает имя файла всегда. Иногда это заканчивается Response has no content.

может ли кто-нибудь дать мне знать, что я делаю неправильно здесь?

2 ответов


нет необходимости использовать стандартные блокирующие потоки, когда у вас есть реактивные потоки для этой цели:

  path("fileUpload") {
    post {
      fileUpload("fileUpload") {
        case (fileInfo, fileStream) =>
          val sink = FileIO.toPath(Paths.get("/tmp") resolve fileInfo.fileName)
          val writeResult = fileStream.runWith(sink)
          onSuccess(writeResult) { result =>
            result.status match {
              case Success(_) => complete(s"Successfully written ${result.count} bytes")
              case Failure(e) => throw e
            }
          }
      }
    }
  }

этот код будет загружать fileUpload multipart поле в файл внутри


Если вам нужно только загрузить файл, но ничего не делать, пока загрузка не закончится в потоке файлов, то есть гораздо более простой способ:

def tempDestination(fileInfo: FileInfo): File =
  File.createTempFile(fileInfo.fileName, ".tmp")

val route =
  storeUploadedFile("csv", tempDestination) {
    case (metadata, file) =>
      // do something with the file and file metadata ...
      file.delete()
      complete(StatusCodes.OK)
  }

см. документы: https://doc.akka.io/docs/akka-http/current/routing-dsl/directives/file-upload-directives/storeUploadedFile.html