You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GridFS仅保存文件信息至files集合,未存储chunks问题求助

问题分析与解决方案

问题现象

使用GridFS与FastAPI交互的工具类时,调用GetFile返回的生成器无数据;数据库中files集合存在文件记录,但chunks集合无对应分片数据。

核心原因

  1. UploadFile文件指针问题:FastAPI的UploadFile的file属性是SpooledTemporaryFile,直接传入upload_from_stream时,文件指针可能已处于末尾,导致没有内容被写入GridFS。
  2. 类型注解错误:StoreFile方法中uploader参数的类型注解为int,但默认值是字符串"Unknown",会引发类型不兼容问题。
  3. 同步文件对象适配问题:Motor的upload_from_stream期望异步流式读取,但UploadFile.file是同步文件对象,可能无法正确完成异步上传。

修复后的代码

from motor.motor_asyncio import AsyncIOMotorGridFSBucket
from fastapi import UploadFile
from bson import ObjectId
from bson.errors import InvalidId
from pymongo.errors import PyMongoError


async def ChunkGenerator(grid_out):
    while True:
        chunk = await grid_out.readchunk()
        if not chunk:
            break
        yield chunk


async def GetFile(evidence_id: str, db):
    try:
        fs = AsyncIOMotorGridFSBucket(db)
        file = await fs.open_download_stream(ObjectId(evidence_id))
        content_type = file.metadata.get("content_type", "application/octet-stream")
        return ChunkGenerator(file), content_type
    except (InvalidId, FileNotFoundError, PyMongoError):
        return None


async def StoreFile(file: UploadFile, db, uploader: str = "Unknown"):
    try:
        fs = AsyncIOMotorGridFSBucket(db)
        # 异步读取UploadFile的全部内容
        file_content = await file.read()
        file_id = await fs.upload_from_stream(
            file.filename,
            file_content,
            metadata={"uploader": uploader, "content_type": file.content_type},
        )
        return str(file_id)
    except PyMongoError:
        return None


async def DeleteFile(evidence_id: str, db):
    try:
        fs = AsyncIOMotorGridFSBucket(db)
        await fs.delete(ObjectId(evidence_id))
        return True
    except (InvalidId, FileNotFoundError, PyMongoError):
        return False

关键修复点

  • 修正类型注解:将uploader的类型注解改为str,匹配默认值类型,避免类型错误。
  • 异步读取文件内容:使用await file.read()异步获取UploadFile的全部内容,确保文件指针从开头读取,且内容完整传入GridFS。
  • 简化metadata处理:因为uploader已为字符串类型,无需额外调用str()转换。

额外优化建议

如果上传大文件,一次性读取全部内容可能占用过多内存,可改用异步流式读取的方式:

async def StoreFile(file: UploadFile, db, uploader: str = "Unknown"):
    try:
        fs = AsyncIOMotorGridFSBucket(db)
        # 创建异步上传流
        stream = await fs.open_upload_stream(
            file.filename,
            metadata={"uploader": uploader, "content_type": file.content_type}
        )
        # 分块异步读取并写入
        while chunk := await file.read(1024 * 1024):  # 1MB分块
            await stream.write(chunk)
        await stream.close()
        return str(stream._id)
    except PyMongoError:
        return False

内容的提问来源于stack exchange,提问作者arlo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 10:54:56