FastAPI UploadFile慢速

2022-02-23 00:00:00 python fastapi forms upload

问题描述

您好,我对FastAPI上载文件速度有问题。

如果我如下所示创建和结束:

@app.post("/report/upload")
def create_upload_files(files: UploadFile = File(...)):
        try:
            with open(files.filename,'wb+') as wf:
                wf.write(file.file.read())
                wf.close()
        except Exception as e:
            return {"error": e.__str__()}

使用uvicorn:

启动
../venv/bin/uvicorn test_upload:app --host=0.0.0.0 --port=5000 --reload

我正在做一些测试,根据请求上载一个大约100M的文件,大约需要128秒。

f = open(sys.argv[1],"rb").read()
hex_convert = binascii.hexlify(f)
items = {"files": hex_convert.decode()}
start = time.time()
r = requests.post("http://192.168.0.90:5000/report/upload",files=items)
end = time.time() - start
print(end)

我测试了在flask中编写API端点的相同上传脚本,花费了大约0.5秒

from flask import Flask, render_template, request
app = Flask(__name__)


@app.route('/uploader', methods = ['GET', 'POST'])
def upload_file():
   if request.method == 'POST':
      f = request.files['file']
      f.save(f.filename)
      return 'file uploaded successfully'

if __name__ == '__main__':
    app.run(host="192.168.0.90",port=9000)

我是否做错了什么?

感谢您的帮助


解决方案

终结点应为异步,并且由于所有UploadFilemethods are async methods, you need to "await" them。因此,文件内容应读作:contents = await myfile.read()

您可以使用同步写入方式写入文件,如下所示:

@app.post("/upload")
async def upload(files: List[UploadFile] = File(...)):
    for file in files:
        with open(file.filename, 'wb') as f:
            contents = await file.read()
            f.write(contents)
            
    return {"Uploaded Filenames": [file.filename for file in files]}

或(更好)使用aiofiles的异步写入:

@app.post("/upload")
async def upload(files: List[UploadFile] = File(...)):
    for file in files:
        async with aiofiles.open(file.filename, 'wb') as f:
            contents = await file.read()
            await f.write(contents)
            
    return {"Uploaded Filenames": [file.filename for file in files]}

或异步in the chunked manner, to avoid loading the entire file into memory。不过,此操作需要更长的时间才能完成(取决于您选择的区块大小)。

@app.post("/upload")
async def upload(files: List[UploadFile] = File(...)):
    for file in files:
        async with aiofiles.open(file.filename, 'wb') as f:
            while content := await file.read(1024): # async read chunk
                await f.write(content)
            
    return {"Uploaded Filenames": [file.filename for file in files]}

通过Python请求测试代码:

url = 'http://127.0.0.1:8000/upload'
files = [('files', open('test_files/a.txt', 'rb')), ('files', open('test_files/b.txt', 'rb'))]
resp = requests.post(url=url, files = files) 
print(resp.json())

相关文章