You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Akka-Http文件上传:请求未完全读取就返回响应的问题排查

Akka HTTP 文件上传:请求提前返回的原因与解决方案

Hey Dave, let's unpack why you're seeing those "request not fully read" logs and how to ensure your uploaded files are completely written to disk before sending a response.

What You're Missing

The core misunderstanding here is how Akka HTTP ties request lifecycle to your stream processing:

  • You assumed uploadedF blocks until the sink finishes, but routing logic might be cutting it short: If you return a response (like complete(StatusCodes.OK)) without waiting for the IOResult from your FileIO sink, Akka HTTP will immediately mark the request as handled and close the connection. Your uploadedF future might still be running in the background, but the request context is already gone—hence the "not fully read" logs.
  • Stream lifecycle is decoupled from routing by default: Akka Streams processes data asynchronously. If you don't explicitly link your sink's completion to the route's response, Akka HTTP won't wait for the file to finish writing. A common mistake looks like this:
    path("upload") {
      post {
        fileUpload("file") {
          case (metadata, byteSource) =>
            val uploadedF = byteSource.runWith(FileIO.toPath(Paths.get("/tmp/" + metadata.fileName)))
            complete(StatusCodes.OK) // Oops—returns immediately, doesn't wait for uploadedF
        }
      }
    }
    
    Here, the response goes out right away, while the stream is still trying to read the request body and write to disk.

How to Verify Full Request Read & Complete File Write

You need to bind your route's response to the completion of the IOResult future. Here are two solid approaches:

1. Wait for the Sink's IOResult Before Responding

Use Akka HTTP's onSuccess directive to wait for the sink to finish processing, then return a response based on the result. This ensures Akka HTTP keeps the request open until the file is fully written and the request body is fully read:

path("upload") {
  post {
    fileUpload("file") {
      case (metadata, byteSource) =>
        val targetPath = Paths.get("/tmp/" + metadata.fileName)
        val uploadedF = byteSource.runWith(FileIO.toPath(targetPath))
        
        onSuccess(uploadedF) { ioresult =>
          if (ioresult.wasSuccessful) {
            complete(s"Success! Wrote ${ioresult.count} bytes to ${metadata.fileName}")
          } else {
            complete(StatusCodes.InternalServerError -> "File upload failed mid-write")
          }
        }
    }
  }
}

Now, the route only completes when the sink's IOResult is available. Akka HTTP will hold the request context until then, so the entire request body is read and the file is safely written.

2. Add Integrity Checks (Bonus)

To be extra sure the file wasn't truncated, validate the written byte count against the expected request size (if provided via the Content-Length header):

path("upload") {
  post {
    extractRequest { req =>
      fileUpload("file") {
        case (metadata, byteSource) =>
          val expectedBytes = req.headers.collectFirst { case h: `Content-Length` => h.length }
          val targetPath = Paths.get("/tmp/" + metadata.fileName)
          val uploadedF = byteSource.runWith(FileIO.toPath(targetPath))
          
          onSuccess(uploadedF) { ioresult =>
            val isComplete = expectedBytes.forall(_ == ioresult.count)
            if (ioresult.wasSuccessful && isComplete) {
              complete(StatusCodes.OK -> "File uploaded fully and verified")
            } else {
              val errorMsg = if (!ioresult.wasSuccessful) "Write failed" else "File truncated"
              complete(StatusCodes.BadRequest -> s"Upload failed: $errorMsg")
            }
          }
      }
    }
  }
}

The Key Idea

Akka HTTP routes are driven by futures—only when the RouteResult future (returned by your route logic) completes does the request get marked as done. By tying your complete call to the sink's IOResult future, you're telling Akka HTTP: "Don't finish this request until the file is fully written and the entire request body is read."

内容的提问来源于stack exchange,提问作者Dave Lugg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:18:41