如何在FastAPI中将文件读入内存并将其传递给MarkItDown库?

问题描述 投票:0回答:2

需要将文件上传到 FastAPI 端点,将其转换为 Markdown 并将文本保存到 Redis(文件大小最大为 4mb)。

到目前为止我发现的唯一逻辑是将文件上传为

UploadFile
,读取内容,使用正确的扩展名将它们保存到磁盘,将该路径传递到https://github.com/microsoft/markitdown库,再次读取该 markdown 文件,然后将其传递给 Redis。 I/O 太多了。 有没有办法在内存中完成所有这些?

(为了代码简单起见,我删除了所有错误处理,我假设只有文本文件)

@router.post("/upload")
async def uploadPost(filepond: UploadFile = File()):
    """
    Convert a textual file to markdown.
    Store in Redis 
    """
    # Create a temporary file to save the uploaded content 
    # for sake of simplicity I use txt for everything
    with NamedTemporaryFile(delete=False,suffix=".txt") as temp_file:
        temp_file_path = temp_file.name
        content = await filepond.read()
        temp_file.write(content)
        temp_file.close()

        md = MarkItDown()
        result = md.convert(temp_file_path)
        redis.setex("some key", 3600, result.text_content)

    os.remove(temp_file_path)
        
python python-3.x file fastapi markitdown
2个回答
1
投票

您似乎受到当前使用的库的限制,不是 FastAPI,它提供了一种方法来在请求正文到达时以块的形式获取(使用

request.stream()
而不是
UploadFile
)—请参阅这个答案这个答案

您正在使用的库包含一个

convert_stream()
方法,但它似乎并没有按照名称实际含义进行操作。
stream
参数(没有明确的类型)用于一次读取全部内容,并将它们简单地存储到临时文件中(本质上,类似于您当前的方法)。

考虑到库的限制,您可能仍然会受益于使用

request.stream()
(即使文件大小高达 4MB,正如您所提到的,可能并不那么明显)在块到达时写入它们直接使用
NamedTemporaryFile
与使用
UploadFile
相比,后者会将大于 1MB 的文件存储到您可以使用的
SpooledTemporaryFile
稍后需要阅读其中的内容,如此答案中所述。因此,您至少会避免不必要地写入和读取两个临时文件,如问题中提供的示例所示。类似的例子可以在这里这里这里找到。

示例

from fastapi import FastAPI, Request, HTTPException
from fastapi.concurrency import run_in_threadpool
from tempfile import NamedTemporaryFile
import aiofiles
import os

app = FastAPI()

    
@app.post('/upload')
async def upload(request: Request):
    try:
        async with aiofiles.tempfile.NamedTemporaryFile("wb", delete=False, suffix=".txt") as temp:
            try:
                async for chunk in request.stream():
                    await temp.write(chunk)
            except Exception:
                raise HTTPException(status_code=500, detail='Something went wrong')
                
         # You could have the `convert` function run in an external ThreadPool/ProcessPool, 
         # in order to avoid blocking the event loop
         md = MarkItDown()
         res = await run_in_threadpool(md.convert, temp.name)
    except Exception:
        raise HTTPException(status_code=500, detail='Something went wrong')
    finally:
        os.remove(temp.name)

0
投票

您对使用 tmpfs 在内存中存储临时文件有何看法?
我认为像“内存临时文件”这样的库值得考虑。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.