使用 Akka HTTP 上传文件

File upload using 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 来生成新的文件名,以确保文件名是唯一的。所以我需要 return 新文件名给调用者。但是,此方法并不总是 returning 文件名。有时,它以 Response has no content 结尾。

谁能告诉我我做错了什么?

当您有用于此目的的反应流时,无需使用标准阻塞流:

  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 多部分字段上传到 /tmp 目录中的文件。它只是将输入源的内容转储到相应的文件接收器,并在完成写入操作时返回一条消息。

您可能还想调整用于 FileIO 源和接收器的调度程序,如 their scaladocs 中所述。

如果您只需要上传一个文件,但在文件流中上传完成之前什么都不做,那么还有更简单的方法:

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