Загрузка файла с использованием 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
.
Может ли кто-нибудь сообщить мне, что я делаю неправильно здесь?
Ответы
Ответ 1
Нет необходимости использовать стандартные блокирующие потоки, если для этой цели есть реактивные потоки:
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 в файл внутри каталога /tmp
. Он просто выгружает содержимое входного источника в соответствующий файловый приемник, возвращая сообщение после завершения операции записи.
Вы также можете настроить диспетчер, используемый для источников и стоков FileIO
, как описано в их скайдадоках.
Ответ 2
Если вам нужно только загрузить файл, но ничего не делать, пока загрузка не закончится в потоке файлов, то есть гораздо более простой способ:
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.
Ответ 3
Извините, я новичок и у меня проблема:
def insertPhoto(databaseDataProvider: DatabaseDataProvider, imagepath: String, data: Source[akka.util.ByteString, Any], token: String, event_id: String) = {
var savefile = imagepath
val dbuser = databaseDataProvider.existToken(token)
if (dbuser.get != null) {
val vyrobnicislo = dbuser.get.vyrobnicislo
val iddb = dbuser.get.iddatabase
val podtrzitko = "_"
savefile = s"$savefile\\$vyrobnicislo\\doc$iddb$podtrzitko$event_id.jpg"
implicit val materializer = ActorMaterializer.create(cz.evotech.QuickstartServer.system)
val file = Paths.get(savefile)
data.runWith(FileIO.toPath(file))
}
}
Размер сохраненного изображения равен 0. Какую ошибку я делаю? Пожалуйста спросите