GridFS仅保存文件信息至files集合,未存储chunks问题求助
问题分析与解决方案
问题现象
使用GridFS与FastAPI交互的工具类时,调用GetFile返回的生成器无数据;数据库中files集合存在文件记录,但chunks集合无对应分片数据。
核心原因
- UploadFile文件指针问题:FastAPI的
UploadFile的file属性是SpooledTemporaryFile,直接传入upload_from_stream时,文件指针可能已处于末尾,导致没有内容被写入GridFS。 - 类型注解错误:
StoreFile方法中uploader参数的类型注解为int,但默认值是字符串"Unknown",会引发类型不兼容问题。 - 同步文件对象适配问题: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
相关产品推荐
相关产品推荐

