Загрузка файлов с помощью 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