Web服务器不仅要处理API请求、HTML等传统内容,还应该具备处理双向文件传输的能力。对于体积庞大的文件,服务器需要在系统内存占用最小化的情况下完成传输。
此外,服务器还应提供对文件目录的访问权限,包括任意深度的子目录,并能妥善处理静态文件。
为了高效处理大文件,FastAPI的上传和下载功能需要以下额外模块支持:
- 安装pip python-multipart模块
- 安装AIO文件处理模块
FastAPI主要面向API开发,本书多数示例采用JSON请求和响应格式。但在最后一章中,我们接触到了表格数据,其处理方式有所不同。本章将介绍文件类型,它在某些方面类似于表单数据。
FastAPI提供了两种文件上传技术:File对象和UploadFile对象。
File对象用于直接文件上传。您的路径函数可以是同步的(def定义)或异步的(async定义),但异步版本更优,因为它在上传文件时不会阻塞Web服务器。
FastAPI会将文件分割成多个块并在内存中重新组装,因此File对象更适合处理较小文件。FastAPI不再假设输入是JSON,而是将文件编码为表单元素。
现在让我们编写代码来请求文件并进行测试。您可以从本地计算机选择任何文件进行测试,或者从网络资源下载一个。我下载了一个1KB的文件,并本地保存为small_file@1KB.bin。
在示例19-1中,向您的代码顶部添加以下内容:
启动Uvicorn后,尝试使用示例19-2中的Httpie进行测试:
需要注意以下几点:
- 需要使用-f(或–form)参数,因为文件以表单形式上传,而非JSON文本。
- small_file@1KB.bin:
- small_file:与示例19-1中的FastAPI路径函数名称匹配。
- @:Httpie用于创建表单的简写符号。
- 1KB.bin:正在上传的文件。
示例19-3展示了等效的编程测试方法:
对于大文件,推荐使用UploadFile对象。这将创建一个Python伪异步临时文件对象,主要存储在服务器磁盘上而非内存中。该对象支持read()、write()和seek()方法。示例19-4展示了这一用法,并使用异步定义而非同步定义,以避免在上传过程中阻塞Web服务器。
File对象创建字节对象并需要括号。UploadFile则是另一类对象。
如果Uvicorn的启动过程尚未完成,现在是进行测试的好时机。这次示例19-5和19-6使用了一个千兆字节的文件,我从中获取了该文件。
遗憾的是,重力不会加速文件下载。相反,我们有了上传方法的对应项。
首先,在示例19-7中,是一次性版本的文件响应:
这里有一个测试方法。首先,将文件放置在与响应相同的目录中。现在,使用示例19-8:
如果您对抑制消息表示怀疑,示例19-9通过管道将输出传输到像WC这样的工具,以确保您返回了1,000字节:
与文件上传类似,下载大文件最好使用流式响应,以块的形式返回文件。示例19-10展示了这一点,并使用异步def路径函数,以避免在不使用CPU时阻塞。在此我将省略错误检查;如果文件路径不存在,open()调用将引发异常。
gen_expr是gen_file()返回的。StreamingResponse使用它来迭代内容参数,因此可以分块下载文件。
示例19-11是相应的测试。这需要文件以及响应,并且需要更长时间。
传统Web服务器可以像处理普通文件系统上的文件一样处理服务器文件。FastAPI允许您通过静态文件功能实现这一点。
为此示例,让我们创建一个名为static_files的目录(名称略显无聊),供用户下载。
- 创建一个名为static_files的目录,与main.py处于同一级别。(这可以是任何名称,我这样命名是为了帮助记住我创建它的原因。
- 在其中放入一个名为example.txt的文本文件,内容为ABC :)。
示例19-12将提供任何以/开头(我也可以在这里使用任何文本字符串)的URL,使用目录中的文件:
顶部代码确保我将静态文件与main.py放在同一目录下。__file__变量是此文件的完整路径名。
示例19-13展示了手动测试这种方法:
我传递给静态文件()的html=True参数有什么作用?这使得它更像传统服务器,在URL中未明确要求时返回文件(如果该目录中存在索引文件)。因此,让我们在目录中创建一个index.html文件,内容为Hello World,然后使用示例19-11进行测试:
您可以拥有任意数量的文件(以及包含文件的子目录等),如您所愿。在static_files目录下创建一个subdir目录,并在其中放置两个文件:
- file1.txt:包含文本xyz :(。
- file2.txt:包含文本你是怎么找到我的?
我不会在这里包含这些示例。您可以自行尝试,希望有更丰富的命名创意。
本章展示了如何上传或下载文件——无论是小的、大的,甚至是gigantic的。此外,还展示了如何以怀旧(非API)Web风格从目录中提供文件。