Ari*_*ric 8 python file-upload fastapi
我根据官方文档使用fastapi上传文件,就像:
@app.post("/create_file/")
async def create_file(file: UploadFile=File(...)):
file2store = await file.read()
# some code to store the BytesIO(file2store) to the other database
Run Code Online (Sandbox Code Playgroud)
当我使用 python requests lib 发送请求时:
f = open(".../file.txt", 'rb')
files = {"file": (f.name, f, "multipart/form-data")}
requests.post(url="SERVER_URL/create_file", files=files)
Run Code Online (Sandbox Code Playgroud)
file2store 始终为空。有时(很少见),它可以获取文件字节,但几乎所有时间都是空的,所以我无法在另一个数据库上恢复文件。我还尝试了 'bytes' 而不是 'UploadFile',我得到了相同的结果。我的代码有什么地方不对,还是我使用fastapi上传文件的方式有问题?我google了很长时间,但没有成功。所以我在这里提出问题,希望知道答案的人可以帮助我。谢谢
Chr*_*ris 99
首先,根据FastAPI 文档python-multipart,如果您还没有安装 \xe2\x80\x94,则需要安装\xe2\x80\x94,因为上传的文件作为“表单数据”发送。例如:
pip install python-multipart\nRun Code Online (Sandbox Code Playgroud)\n下面的示例使用对象.file的属性UploadFile来获取实际的 Python 文件(即SpooledTemporaryFile),这允许您调用SpooledTemporaryFile\ 的方法,例如.read()和.close(),而无需使用await它们。但是,def在这种情况下,使用 \xe2\x80\x94 定义端点非常重要,否则,如果端点是使用async def. 在 FastAPI 中,普通def端点在外部线程池中运行,然后等待该线程池,而不是直接调用(因为它会阻塞服务器)。
FastAPI SpooledTemporaryFile/Starlette 使用的max_size属性设置为 1 MB,这意味着数据将在内存中进行假脱机处理,直到文件大小超过 1 MB,此时数据将写入操作系统下磁盘上的临时文件临时目录。因此,如果您上传的文件大于 1 MB,它不会存储在内存中,调用file.file.read()实际上会将数据从磁盘读取到内存中。因此,如果文件太大而无法放入服务器的 RAM,您应该分块读取文件并一次处理一个块,如下面的“分块读取文件”部分所述。您也可以看看这个答案,它演示了另一种分块上传大文件的方法,使用 Starlette 的.stream()方法和streaming-form-data允许解析流的包multipart/form-data流块,从而大大减少上传文件所需的时间。
如果您必须使用async def\xe2\x80\x94 定义端点,就像您可能需要await为路由 \xe2\x80\x94 中的其他协程定义端点一样,那么您应该使用异步读取和写入内容,如本答案所示。此外,如果您需要在上传文件的同时发送其他数据(例如JSON数据),请查看此答案。我还建议您看看这个答案def,它解释了和async def端点之间的区别。
应用程序.py
\npip install python-multipart\nRun Code Online (Sandbox Code Playgroud)\n正如前面和这个答案中所述,如果文件太大而无法放入内存\xe2\x80\x94,例如,如果您有 8GB RAM,则无法加载 50GB 文件(更不用说可用 RAM总是小于您计算机上安装的总量,因为其他应用程序将使用一些 RAM)\xe2\x80\x94您应该将文件分块加载到内存中,并一次处理一个块数据。不过,此方法可能需要更长的时间才能完成,具体取决于您选择的块大小\xe2\x80\x94,在下面的示例中,块大小为1024 * 1024字节(即 1MB)。您可以根据需要调整块大小。
from fastapi import File, UploadFile\n\n@app.post("/upload")\ndef upload(file: UploadFile = File(...)):\n try:\n contents = file.file.read()\n with open(file.filename, \'wb\') as f:\n f.write(contents)\n except Exception:\n return {"message": "There was an error uploading the file"}\n finally:\n file.file.close()\n\n return {"message": f"Successfully uploaded {file.filename}"}\nRun Code Online (Sandbox Code Playgroud)\n另一种选择是使用shutil.copyfileobj(),它用于将一个file-like对象的内容复制到另一个file-like对象(也可以看看这个答案)。默认情况下,数据以块的形式读取,1024 * 1024Windows 的默认缓冲区(块)大小为 1MB(即字节),其他平台为 64KB,如此处的源代码所示。您可以通过传递可选参数来指定缓冲区大小length。注意:如果length传递负值,则将读取文件的全部内容\xe2\x80\x94see f.read(),这.copyfileobj()在幕后使用(如源代码所示)。
from fastapi import File, UploadFile\n \n@app.post("/upload")\ndef upload(file: UploadFile = File(...)):\n try:\n with open(file.filename, \'wb\') as f:\n while contents := file.file.read(1024 * 1024):\n f.write(contents)\n except Exception:\n return {"message": "There was an error uploading the file"}\n finally:\n file.file.close()\n\n return {"message": f"Successfully uploaded {file.filename}"}\nRun Code Online (Sandbox Code Playgroud)\n测试.py
\nfrom fastapi import File, UploadFile\nimport shutil\n \n@app.post("/upload")\ndef upload(file: UploadFile = File(...)):\n try:\n with open(file.filename, \'wb\') as f:\n shutil.copyfileobj(file.file, f)\n except Exception:\n return {"message": "There was an error uploading the file"}\n finally:\n file.file.close()\n \n return {"message": f"Successfully uploaded {file.filename}"}\nRun Code Online (Sandbox Code Playgroud)\n有关 HTML<form>示例,请参阅此处。
应用程序.py
\nimport requests\n\nurl = \'http://127.0.0.1:8000/upload\'\nfile = {\'file\': open(\'images/1.png\', \'rb\')}\nresp = requests.post(url=url, files=file) \nprint(resp.json())\nRun Code Online (Sandbox Code Playgroud)\n正如本答案前面所述,如果您期望一些相当大的文件并且没有足够的 RAM 来容纳从开始到结束的所有数据,您应该将文件分块加载到内存中,从而处理一次一个块的数据(注意:根据需要调整块大小,下面是1024 * 1024字节)。
from fastapi import File, UploadFile\nfrom typing import List\n\n@app.post("/upload")\ndef upload(files: List[UploadFile] = File(...)):\n for file in files:\n try:\n contents = file.file.read()\n with open(file.filename, \'wb\') as f:\n f.write(contents)\n except Exception:\n return {"message": "There was an error uploading the file(s)"}\n finally:\n file.file.close()\n\n return {"message": f"Successfuly uploaded {[file.filename for file in files]}"} \nRun Code Online (Sandbox Code Playgroud)\n或者,使用shutil.copyfileobj():
from fastapi import File, UploadFile\nfrom typing import List\n\n@app.post("/upload")\ndef upload(files: List[UploadFile] = File(...)):\n for file in files:\n try:\n with open(file.filename, \'wb\') as f:\n while contents := file.file.read(1024 * 1024):\n f.write(contents)\n except Exception:\n return {"message": "There was an error uploading the file(s)"}\n finally:\n file.file.close()\n \n return {"message": f"Successfuly uploaded {[file.filename for file in files]}"} \nRun Code Online (Sandbox Code Playgroud)\n测试.py
\nfrom fastapi import File, UploadFile\nfrom typing import List\nimport shutil\n\n@app.post("/upload")\ndef upload(files: List[UploadFile] = File(...)):\n for file in files:\n try:\n with open(file.filename, \'wb\') as f:\n shutil.copyfileobj(file.file, f)\n except Exception:\n return {"message": "There was an error uploading the file(s)"}\n finally:\n file.file.close()\n\n return {"message": f"Successfuly uploaded {[file.filename for file in files]}"} \nRun Code Online (Sandbox Code Playgroud)\n有关 HTML<form>示例,请参阅此处。
小智 5
@app.post("/create_file/")
async def image(image: UploadFile = File(...)):
print(image.file)
# print('../'+os.path.isdir(os.getcwd()+"images"),"*************")
try:
os.mkdir("images")
print(os.getcwd())
except Exception as e:
print(e)
file_name = os.getcwd()+"/images/"+image.filename.replace(" ", "-")
with open(file_name,'wb+') as f:
f.write(image.file.read())
f.close()
file = jsonable_encoder({"imagePath":file_name})
new_image = await add_image(file)
return {"filename": new_image}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
16254 次 |
| 最近记录: |