FastAPI,返回带有 sql 查询输出的 File 响应

Posted

技术标签:

【中文标题】FastAPI,返回带有 sql 查询输出的 File 响应【英文标题】:FastAPI, return a File response with the output of a sql query 【发布时间】:2020-07-23 05:00:38 【问题描述】:

我正在使用 FastAPI,目前我返回了一个 csv,它是从带有 pandas 的 SQL 服务器读取的。 (pd.read_sql()) 但是 csv 对于浏览器来说非常大,我想用 File 响应返回它: https://fastapi.tiangolo.com/advanced/custom-response/(页面结束)。 如果不先将其写入一个 csv 文件,我似乎无法做到这一点,这似乎很慢,并且会在每个请求上用 csv 混淆文件系统。

所以我的问题是,有没有办法从 sql 数据库或 pandas 数据框返回 FileResponse。

如果没有,有没有办法在客户端读取完生成的 csv 文件后删除它?

感谢您的帮助!

亲切的问候,

斯蒂芬

【问题讨论】:

如果您觉得我的回答回答了您的问题,请随时将我的回答标记为正确:) 【参考方案1】:

基于此https://github.com/tiangolo/fastapi/issues/1277

    将您的数据帧转换为流 使用流式响应 修改标题使其成为下载(可选)
    from fastapi.responses import StreamingResponse
    import io
    
    @app.get("/get_csv")
    async def get_csv():
    
       df = pandas.DataFrame(dict(col1 = 1, col2 = 2))
    
       stream = io.StringIO()
    
       df.to_csv(stream, index = False)
    
       response = StreamingResponse(iter([stream.getvalue()]),
                            media_type="text/csv"
       )
    
       response.headers["Content-Disposition"] = "attachment; filename=export.csv"

       return response

【讨论】:

response = StreamingResponse(io.StringIO(df.to_csv(index=False)), media_type="text/csv") 也应该可以工作 这节省了我的时间。【参考方案2】:

在这个问题上,我也在用头撞墙。我的用例略有不同,因为我将图像、pdf 等作为 blob 存储在我的 maria 数据库中。我发现诀窍是将 blob 内容传递给 BytesIO,其余的很简单。

from fastapi.responses import StreamingResponse
from io import BytesIO

@router.get('/attachment/id')
async def get_attachment(id: int):
    mdb = messages(s.MARIADB)

    attachment = mdb.getAttachment(id)
    memfile = BytesIO(attachment['content'])
    response = StreamingResponse(memfile, media_type=attachment['contentType'])
    response.headers["Content-Disposition"] = f"inline; filename=attachment['name']"

    return response

【讨论】:

【参考方案3】:

添加到前面提到的代码中,我发现放置另一个响应标头很有用,以便客户端能够看到“Content-Disposition”。这是因为默认情况下,客户端只能看到 CORS 安全列表中的响应标头。 “Content-Disposition”不在此列表中,因此必须明确添加 https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers。

我不知道是否有另一种方法可以为客户端或服务器以更通用的方式指定它,以便它适用于所有必要的端点,但这是我应用它的方式。

@router.post("/files", response_class = StreamingResponse)
async def anonymization(file: bytes = File(...), config: str = Form(...)):
    # file as str
    inputFileAsStr = StringIO(str(file,'utf-8'))
    # dataframe
    df = pd.read_csv(inputFileAsStr)
    # send to function to handle anonymization
    results_df = anonymize(df, config)
    # output file
    outFileAsStr = StringIO()
    results_df.to_csv(outFileAsStr, index = False)
    response = StreamingResponse(
        iter([outFileAsStr.getvalue()]),
        media_type='text/csv',
        headers=
            'Content-Disposition': 'attachment;filename=dataset.csv',
            'Access-Control-Expose-Headers': 'Content-Disposition'
        
    )
    # return
    return response

【讨论】:

以上是关于FastAPI,返回带有 sql 查询输出的 File 响应的主要内容,如果未能解决你的问题,请参考以下文章

SQL 中 EXISTS 与 NOT EXISTS

对于带有 json 列的 sqlite db,fastapi 响应的格式不正确

查询返回带有错误参数的 sql 字符串

优化 SQL 查询以返回带有标签的记录

带有连接的休眠自定义 SQL 查询 - 避免返回数组列表

带有参数的 PL/SQL 过程/函数从选择查询返回表