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

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

提问人:aayushmittall 提问时间:6/23/2021 更新时间:7/7/2021 访问量:15857

问:

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

python http 服务器端 fastapi

评论

0赞 MatsLindh 6/23/2021
据我所知,没有实际限制:github.com/encode/starlette/issues/890 - 请参阅 github.com/tiangolo/fastapi/issues/362 了解如何实现基于标头的限制Content-Length
0赞 aayushmittall 6/23/2021
感谢您的回答,是否也没有任何 HTTP 有效负载大小限制?
0赞 MatsLindh 6/23/2021
如果你正在考虑 POST 大小,这些票证中会讨论 - 但这取决于你是直接在网络上通过 FastAPI/Starlette 提供请求,还是先通过 nginx 或类似方式。链的任何部分都可能对允许的尺寸进行限制。
0赞 Chris 8/29/2022
请查看此答案,了解如何使用 .stream() 在块中读取请求正文,从而在您的应用程序中强制执行正文/文件大小限制。

答:

16赞 Simba 6/23/2021 #1

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

反向代理

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

对于 Apache,正文大小可以通过 控制,默认为 0。LimitRequestBody

ASGI 服务器

ASGI 服务器没有正文大小限制。至少 gunicorn、uvicorn、hypercorn 是这种情况。

引自 Hypercorn 文档。

大型请求正文

这种攻击属于第二种类型,旨在通过邀请服务器接收大型请求正文(从而将正文写入内存)来耗尽服务器的内存。配置不当的服务器对请求正文大小没有限制,并且可能允许单个请求耗尽服务器。

由框架来防范这种攻击。这是为了允许框架在需要时使用请求正文。

注意:Gunicorn 不限制请求正文的大小,但限制请求行和请求标头的大小。

  • --limit-request-line,每个 req 行的大小限制,默认 4096
  • --limit-request-fields,标题字段数,默认值为 100
  • --limit-request-field_size,headef 字段的大小,默认值为 8190

ASGI 应用程序/框架

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

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”错误,请检查反向代理。