如何使用Akka HTTP处理Multipart POST请求并获取文件字节数组与文件名?
解决Akka HTTP处理Multipart文件上传并获取文件名与字节数组的问题
你的问题出在当前路由把整个Multipart请求体当成纯文本字符串读取了,所以才会拿到包含Content-Disposition头的完整内容。要正确解析文件上传请求,需要用Akka HTTP的Multipart.FormData实体类型来处理,下面是具体的实现方案:
正确的路由实现
import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.model.Multipart import akka.stream.scaladsl.Sink import scala.concurrent.Future import java.nio.file.Paths import akka.stream.IOResult val routePutData = path("api" / "putFile" / Segment) { subDir => entity(as[Multipart.FormData]) { formData => // 遍历表单中的每个part,提取文件信息 val fileProcessing: Future[(String, Array[Byte])] = formData.parts.mapAsync(1) { // 匹配文件类型的part case filePart: Multipart.FormData.FilePart => val fileName = filePart.filename.getOrElse("unnamed-file") // 将文件流转换为字节数组 filePart.entity.dataBytes .runWith(Sink.fold(Array.empty[Byte])(_ ++ _)) .map(fileName -> _) // 忽略非文件类型的表单字段 case _ => Future.successful(("non-file-part", Array.empty[Byte])) }.runWith(Sink.head) // 假设只上传单个文件,多文件可改用Sink.seq收集 // 等待文件处理完成,返回结果或执行后续逻辑 onSuccess(fileProcessing) { case (fileName, fileBytes) => // 这里可以根据subDir参数将文件保存到指定目录,或者处理字节数组 // 示例:保存文件到磁盘 // val saveResult: Future[IOResult] = filePart.entity.dataBytes.runWith(FileIO.toPath(Paths.get(subDir, fileName))) complete(s"成功接收文件:$fileName,文件大小:${fileBytes.length} 字节") } } }
关键细节说明
- 区分FilePart与普通字段:表单中的文件上传会被解析为
Multipart.FormData.FilePart,而普通文本字段是Multipart.FormData.BodyPart,所以我们需要匹配FilePart来提取文件信息。 - 文件名处理:
filePart.filename是Option[String],因为客户端可能未提供文件名,所以用getOrElse设置默认值。 - 字节数组转换:文件内容以流的形式存在于
filePart.entity.dataBytes中,通过Sink.fold可以把流中的字节片段合并成完整的字节数组。如果处理大文件,建议直接用流写入磁盘(示例中注释的FileIO.toPath方式),避免内存溢出。 - 多文件处理:如果需要支持多文件上传,只需把
runWith(Sink.head)改为runWith(Sink.seq),然后处理Future[Seq[(String, Array[Byte])]]即可。
常见误区修正
你之前尝试的formFields指令是用来处理普通的表单文本字段的,并不适用于文件上传场景,所以无法获取文件内容。必须通过遍历formData.parts来识别并处理文件类型的part。
内容的提问来源于stack exchange,提问作者Gumada Yaroslav
相关产品推荐
相关产品推荐

