我们可以在 FastAPI 中接收的上传文件的最大大小是多少?

What is the maximum size of upload file we can receive in FastAPI?

我正在尝试计算我的客户端可以上传的最大文件大小,以便我的 python fastapi 服务器可以毫无问题地处理它。

您的请求没有直接到达 ASGI 应用程序。它在由 ASGI 应用程序处理之前经过反向代理(Nginx、Apache)、ASGI 服务器(uvicorn、hypercorn、gunicorn)。

反向代理

对于 Nginx,body 大小由 client_max_body_size 控制,默认为 1MB。

对于 Apache,body 大小可以由 LimitRequestBody 控制,默认为 0。

ASGI 服务器

ASGI 服务器没有 body 大小的限制。至少 gunicorn、uvicorn、hypercorn 是这样。

引自 Hypercorn 文档。

Large request body

This attack is of the second type and aims to exhaust the server’s memory by inviting it to receive a large request body (and hence write the body to memory). A poorly configured server would have no limit on the request body size and potentially allow a single request to exhaust the server.

It is up to the framework to guard against this attack. This is to allow the framework to consume the request body if desired.

注意:Gunicorn 不限制请求的大小 body,但限制请求行和请求的大小 header。

  • --limit-request-line,每个请求行的大小限制,默认 4096
  • --limit-request-fields,字段数header,默认100
  • --limit-request-field_size,headef字段的大小,默认8190

ASGI App/Framework

因为 FastAPI 基于 Starlette。如何阅读 body 由 Starlette 处理。从源代码(0.14.3)中读取,请求似乎也没有限制body。

class Request(HTTPConnection):
    ...
    async def stream(self) -> typing.AsyncGenerator[bytes, None]:
        if hasattr(self, "_body"):
            yield self._body
            yield b""
            return

        if self._stream_consumed:
            raise RuntimeError("Stream consumed")

        self._stream_consumed = True
        while True:
            message = await self._receive()
            if message["type"] == "http.request":
                body = message.get("body", b"")
                if body:
                    yield body
                if not message.get("more_body", False):
                    break
            elif message["type"] == "http.disconnect":
                self._is_disconnected = True
                raise ClientDisconnect()
        yield b""

    async def body(self) -> bytes:
        if not hasattr(self, "_body"):
            chunks = []
            async for chunk in self.stream():
                chunks.append(chunk)
            self._body = b"".join(chunks)
        return self._body

结论:如果出现413 Payload Too Large错误,请检查反向代理。