装闭 RenoPit 源码解析(04):装修图纸和合同文件上传处理流程

装修闭坑分析的输入既有户型图、效果图,也有合同、报价单和用户文字说明。装闭 RenoPit 的完整上传实现可以在 fthux/RenoPit 查看,本篇重点追踪文件从浏览器进入磁盘和数据库的过程。

一、前端如何组织多文件上传

CreateProjectPage 使用 <input type="file" multiple> 接收多个文件,允许扩展名包括 PDF、PNG、JPG、WEBP、TXT、DOCX 和 Markdown。文件先保存在 selectedFiles 状态中,页面显示名称、大小和图片或文档图标。

项目创建成功后,前端把所有文件追加到同一个 FormData:

tsx 复制代码
const formData = new FormData()
selectedFiles.forEach((file) => formData.append('files', file))

await fetch(`${API}/projects/${projectId}/upload`, {
  method: 'POST',
  body: formData,
})

项目详情页后续追加素材时也调用同一接口。上传成功后,它并行刷新文件、图片和项目信息,使数量与列表立即更新。

二、后端先按扩展名分流

upload_files() 接收 List[UploadFile],然后逐个读取扩展名。PNG、JPG、JPEG、WEBP、GIF 和 BMP 被视为图片,其余文件进入普通文档分支。
flowchart TD AUploadFile --> B{是否图片扩展名} B -->|是| Csave_upload_file: images C --> Dcompress_image D --> Eget_image_dimensions E --> FProjectImage B -->|否| Gsave_upload_file: files G --> Hextract_text H --> IProjectFile

两条分支最后在同一个数据库事务中提交,并返回本次保存的图片数和文件数。

三、文件如何生成磁盘路径

file_storage.py 不直接使用用户上传的文件名作为磁盘名。generate_storage_filename() 先校验扩展名,再生成 UUID 文件名,避免同名文件互相覆盖。

项目目录按如下形式组织:

text 复制代码
uploads/
└── projects/
    └── {project_id}/
        ├── images/
        │   └── {uuid}.jpg
        └── files/
            └── {uuid}.pdf

save_upload_file() 读取 UploadFile 内容,计算字节数并写入目标路径,最后返回存储文件名、完整路径和文件大小。数据库中仍保存 original_filename,所以页面展示的是用户原始文件名,下载和分析使用的则是唯一存储路径。

四、图片上传后立即预处理

图片落盘后会调用 compress_image()。函数使用 Pillow 打开图片,读取宽高;如果任一边超过最大尺寸,就按比例缩放,并使用 LANCZOS 重采样。

带透明通道的 RGBA 或调色板图片会先转换为 RGB,最终以指定 JPEG 质量原地保存。函数还会尽量保留 EXIF 数据,避免手机照片方向信息丢失。

压缩完成后,get_image_dimensions() 再读取最终宽高,创建 ProjectImage:

python 复制代码
img = ProjectImage(
    project_id=project_id,
    original_filename=file.filename,
    storage_path=storage_path,
    file_size=file_size,
    width=width,
    height=height,
)

后续多模态分析会再次读取该路径,把图片转换成 Base64 后发送给 LLM。

五、文档如何提取纯文本

普通文件落盘后交给 extract_text()。它使用一个扩展名到解析函数的映射表:

  • TXT、Markdown:按 UTF-8 读取;
  • DOCX:通过 python-docx 遍历段落;
  • PDF:通过 PyMuPDF 提取各页文本。

提取结果写入 ProjectFile.extracted_text。如果扩展名不在支持列表,extract_text() 抛出 ValueError,上传接口捕获后将提取文本设为空字符串,文件记录仍然可以保存。

这里处理的是 PDF 中的文本层;PDF 内嵌图片不会自动成为 ProjectImage。因此前端也会提示用户:需要视觉分析的图纸最好直接上传图片格式。

六、预览、下载和删除共用文件记录

项目详情页通过 /files 和 /images 获取元数据。图片预览、图片下载和文件下载分别请求带资源 ID 的接口,后端根据数据库中的 storage_path 返回 FileResponse。

删除单个素材时,接口先检查资源确实属于当前项目,再删除磁盘文件和数据库记录。前端成功后刷新对应列表,image_count 和 file_count 也会由项目响应重新计算。

七、上传链路小结

RenoPit 把上传过程拆成"浏览器收集、扩展名分流、唯一命名落盘、图片压缩或文本提取、数据库登记"五个阶段。最终,图片以磁盘路径和尺寸进入多模态链路,文档以磁盘路径和 extracted_text 进入文本分析链路。

完整实现位于 fthux/RenoPit 的 file_storage.py、image_processor.py、file_parser.py 和项目 API 中。下一篇将从上传后的项目状态出发,分析 FastAPI 如何把耗时 AI 任务交给 Celery Worker。

相关推荐
维克兜率天4 分钟前
【维克】指数调整:哪些股票会被“强制买入“?
人工智能·python·算法
塞讯科技17 分钟前
13,500 个分支被植入窃密工作流:Gheist 如何用 GitHub 令牌搬走仓库密钥
网络安全·ai·github·软件供应链安全·供应链投毒
梧桐16819 分钟前
14天、31个缺陷、28个测试脚本—用 Claude Code 完成第三方软件测试
人工智能·测试工具
罗西的思考42 分钟前
【轻量化复刻大模型】线性注意力交互式演示
人工智能·算法·机器学习
美狐美颜sdk1 小时前
视频美颜SDK中的YUV与RGBA格式有什么区别?直播APP开发解析
人工智能·计算机视觉·音视频·美颜sdk·直播美颜sdk·视频美颜sdk
jimmyleeee1 小时前
Agent安全之七:当智能体之间开始“互相信任”------深入理解 OWASP ASI07 不安全的智能体间通信
人工智能·安全
二哈赛车手1 小时前
新人笔记---hermes定时功能设计,具体讲解以及可优化点
java·人工智能·笔记·spring·ai·typescript
Fluxproxy1 小时前
大模型批量数据采集:AI 自动化工作流搭建实战
大数据·人工智能·自动化