首先,根据
FastAPI documentation
,您需要安装
python-multipart
如果你还没有,上传的文件会作为“表单数据”发送。例如:
pip install python-multipart
以下示例使用
.file
的属性
UploadFile
对象以获得实际的Python文件(即。,
SpooledTemporaryFile
),允许您拨打
假脱机临时文件
的方法,例如
.read()
和
.close()
,而不必
await
他们但是,使用定义端点是很重要的
def
在这种情况下,否则,如果端点定义为
async def
。在FastAPI中,正常
def
端点
is run in an external threadpool that is then awaited, instead of being called directly (as it would block the server)
.
这个
假脱机临时文件
FastAPI/Starlette使用的
max_size
属性设置为1MB,这意味着数据将在内存中假脱机,直到文件大小超过1MB,此时数据将写入操作系统临时目录下磁盘上的临时文件。因此,如果您上传了一个大于1MB的文件,它将不会存储在内存中,并调用
file.file.read()
实际上会将数据从磁盘读取到内存中。因此,如果文件太大,无法放入服务器的RAM,您应该按块读取文件,然后一次处理一个块,如下面的“按块读取”部分所述。你也可以看看
this answer
,展示了另一种方法
分块上传一个大文件
,使用Starlette的
.stream()
方法和
streaming-form-data
允许解析流的包
multipart/form-data
块,这大大减少了上传文件所需的时间。
如果必须使用定义端点
异步定义
因为你可能需要
等候
对于路线内的其他协同活动,您应该使用
异步的
内容的阅读和书写,如中所示
this answer
。此外,如果您需要发送其他数据(例如
JSON
数据)以及上传文件,请查看
this answer
。我还建议你看看
this answer
,这解释了
def
和
异步定义
端点。
上载单个文件
app.py
from fastapi import File, UploadFile
@app.post("/upload")
def upload(file: UploadFile = File(...)):
try:
contents = file.file.read()
with open(file.filename, 'wb') as f:
f.write(contents)
except Exception:
return {"message": "There was an error uploading the file"}
finally:
file.file.close()
return {"message": f"Successfully uploaded {file.filename}"}
分块读取文件
如前面和中所述
this answer
,如果文件太大,无法放入内存例如,如果您有8GB的RAM,则无法加载50GB的文件(更不用说可用的RAM总是小于机器上安装的总容量,因为其他应用程序将使用部分RAM),您应该将文件分块加载到内存中,并一次处理一块数据。然而,这种方法可能需要更长的时间才能完成,这取决于您在下面的示例中选择的块大小,块大小为
1024 * 1024
字节(即1MB)。您可以根据需要调整区块大小。
from fastapi import File, UploadFile
@app.post("/upload")
def upload(file: UploadFile = File(...)):
try:
with open(file.filename, 'wb') as f:
while contents := file.file.read(1024 * 1024):
f.write(contents)
except Exception:
return {"message": "There was an error uploading the file"}
finally:
file.file.close()
return {"message": f"Successfully uploaded {file.filename}"}
另一种选择是使用
shutil.copyfileobj()
,用于复制的内容
file-like
反对另一个
像文件一样
对象(看看
this answer
也默认情况下以默认缓冲区(组块)大小为1MB的组块读取数据(即。,
1024 * 1024
字节),对于其他平台为64KB,如源代码所示
here
。您可以通过传递可选的
length
参数注:如果为负数
长
值时,将读取文件的全部内容
f.read()
以及
.copyfileobj()
在引擎盖下使用(如源代码中所示
here
).
from fastapi import File, UploadFile
import shutil
@app.post("/upload")
def upload(file: UploadFile = File(...)):
try:
with open(file.filename, 'wb') as f:
shutil.copyfileobj(file.file, f)
except Exception:
return {"message": "There was an error uploading the file"}
finally:
file.file.close()
return {"message": f"Successfully uploaded {file.filename}"}
test.py
import requests
url = 'http://127.0.0.1:8000/upload'
file = {'file': open('images/1.png', 'rb')}
resp = requests.post(url=url, files=file)
print(resp.json())
对于HTML
<form>
示例,请参阅
here
.
上载多个文件(列表)
app.py
from fastapi import File, UploadFile
from typing import List
@app.post("/upload")
def upload(files: List[UploadFile] = File(...)):
for file in files:
try:
contents = file.file.read()
with open(file.filename, 'wb') as f:
f.write(contents)
except Exception:
return {"message": "There was an error uploading the file(s)"}
finally:
file.file.close()
return {"message": f"Successfuly uploaded {[file.filename for file in files]}"}
分块读取文件
如本答案前面所述,如果您期望一些相当大的文件,但没有足够的RAM来从头到尾容纳所有数据,则应该将文件分块加载到内存中,从而一次处理一个块的数据(注意:根据需要调整块大小,如下所示
1024 * 1024
字节)。
from fastapi import File, UploadFile
from typing import List
@app.post("/upload")
def upload(files: List[UploadFile] = File(...)):
for file in files:
try:
with open(file.filename, 'wb') as f:
while contents := file.file.read(1024 * 1024):
f.write(contents)
except Exception:
return {"message": "There was an error uploading the file(s)"}
finally:
file.file.close()
return {"message": f"Successfuly uploaded {[file.filename for file in files]}"}
或者,使用
shutil.copyfileobj()
:
from fastapi import File, UploadFile
from typing import List
import shutil
@app.post("/upload")
def upload(files: List[UploadFile] = File(...)):
for file in files:
try:
with open(file.filename, 'wb') as f:
shutil.copyfileobj(file.file, f)
except Exception:
return {"message": "There was an error uploading the file(s)"}
finally:
file.file.close()
return {"message": f"Successfuly uploaded {[file.filename for file in files]}"}
test.py
import requests
url = 'http://127.0.0.1:8000/upload'
files = [('files', open('images/1.png', 'rb')), ('files', open('images/2.png', 'rb'))]
resp = requests.post(url=url, files=files)
print(resp.json())
对于HTML
<表单>
示例,请参阅
在这里
.