
使用 sqlalchemy-file 的 FileField 时,默认保存的文件名为 "unnamed",导致下载时无法识别原始名称;解决方法是显式传入 sqlalchemy_file.File 实例,并设置 filename 参数。
使用 `sqlalchemy-file` 的 `filefield` 时,默认保存的文件名为 `"unnamed"`,导致下载时无法识别原始名称;解决方法是显式传入 `sqlalchemy_file.file` 实例,并设置 `filename` 参数。
在 FastAPI + SQLAlchemy 项目中,sqlalchemy-file 提供了便捷的文件存储能力,但其 FileField 并不会自动继承 UploadFile.filename —— 它仅接收原始字节内容(如 await upload_file.read()),因此必须手动构造一个带元信息的 File 对象。
✅ 正确做法:使用 sqlalchemy_file.File 显式指定文件名
你需要从 sqlalchemy_file 导入 File 类,并将原始文件内容与期望的文件名一并封装:
from sqlalchemy_file import FileField, File # 注意导入 File
# 模型定义保持不变
class UserDoc(Base):
__tablename__ = "user_doc"
id = Column(Integer, primary_key=True)
file = Column(FileField)
在路由处理逻辑中,不要直接传入 file_contents 字节流,而是构建 File 实例:
@router.post("/doc_upload")
async def document_upload(
user_file: UploadFile = File(...),
db: Session = Depends(deps.get_db),
):
try:
file_contents = await user_file.read()
# ✅ 关键:用 File 封装内容 + 自定义 filename
document_data = {
"file": File(
content=file_contents,
filename=user_file.filename or "unnamed.pdf", # 推荐保留原始名
content_type=user_file.content_type
)
}
db_document = UserDoc(**document_data) # 注意类名一致性(原文 UserDocument → UserDoc)
db.add(db_document)
db.commit()
db.refresh(db_document)
return {"message": "File uploaded successfully", "id": db_document.id}
except Exception as e:
db.rollback()
raise HTTPException(status_code=500, detail=f"Upload failed: {str(e)}")
? 注意字段映射:File 构造函数支持 content(bytes)、filename(str)、content_type(str)等参数。若不传 content_type,系统会尝试基于扩展名推断;显式传入更可靠。
? 补充说明与最佳实践
-
原始文件名安全处理:生产环境建议对 user_file.filename 进行清洗(如移除路径、特殊字符),防止目录遍历或 XSS 风险:
from pathlib import Path safe_filename = Path(user_file.filename).name # 剥离路径
-
文件大小限制:sqlalchemy-file 默认不限制大小,建议在 FastAPI 层校验:
if user_file.size > 10 * 1024 * 1024: # 10MB raise HTTPException(400, "File too large") 数据库字段值验证:成功保存后,可检查 db_document.file.filename 是否已正确写入,确保后续生成下载响应时能准确设置 Content-Disposition 头。
通过以上改造,生成的 JSON 元数据中 filename 将不再是 "unnamed",而是你指定的真实名称(如 "report.pdf"),从而保障前端下载时显示合理文件名,提升用户体验与系统健壮性。











