# 图片处理流程文档 **版本**: v1.0.0 **更新日期**: 2026-03-16 **作者**: 菜鸟小 D 🤖 --- ## 📊 完整流程图 ``` 用户上传图片 ↓ [1] 前端上传组件 ↓ [2] 后端接收验证 ↓ [3] 文件命名处理 ↓ [4] 保存到服务器 ↓ [5] 数据库记录 ↓ [6] 返回图片 URL ``` --- ## 1️⃣ 前端上传组件 ### 上传页面 **文件**: `frontend/src/pages/Add.jsx` **上传逻辑**: ```jsx // 选择图片后自动上传 const handleImageSelect = async (e) => { const file = e.target.files[0] if (!file) return const formData = new FormData() formData.append('file', file) formData.append('collection_id', collectionId) const res = await fetch('/api/ocr/recognize', { method: 'POST', body: formData }) const data = await res.json() // 处理 OCR 识别结果 } ``` ### 图片显示 **文件**: `frontend/src/pages/Detail.jsx` **显示逻辑**: ```jsx {img.originalName} { // 加载失败显示"无图片"占位符 e.target.style.display = 'none'; e.target.parentElement.innerHTML = '
无图片
'; }} /> ``` --- ## 2️⃣ 后端接收验证 ### API 端点 **文件**: `backend/app/routers/collections.py` **路由**: `POST /api/collections/upload-image` ### 验证流程 ```python @router.post("/upload-image") async def upload_image( collection_id: str = None, file: UploadFile = File(...), current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): ``` ### 验证步骤 1. **验证藏品是否存在** ```python collection = db.query(Collection).filter( Collection.f99_90_id == collection_id ).first() if not collection: raise HTTPException(status_code=404, detail="E00033: 藏品不存在") ``` 2. **获取用户信息** ```python owner = db.query(User).filter( User.f99_90_id == collection.f99_91_user_id ).first() username = owner.f01_01_name if owner else "unknown" ``` 3. **获取藏品信息** ```python code = collection.f01_02_code or "0000" prefix_serial = collection.f02_10_prefix_serial or "" ``` 4. **验证文件类型** ```python if not file.content_type.startswith('image/'): raise HTTPException(status_code=400, detail="E00038: 只能上传图片文件") ``` 5. **验证文件大小** ```python file_size = len(content) if file_size > 10 * 1024 * 1024: # 10MB raise HTTPException(status_code=400, detail=f"图片大小不能超过 10MB") ``` --- ## 3️⃣ 文件命名处理 ### 命名规则 **格式**: `用户名 - 藏品编号 - 冠字号。扩展名` **示例**: - `admin-0001-J051963351.jpeg` - `admin-0002-J035161361.JPG` - `testuser-0015.jpeg` (无冠字号) ### 命名代码 ```python # 清理特殊字符,只保留字母、数字、中文、横杠 import re clean_username = re.sub(r'[^\w\u4e00-\u9fff\-]', '', username) clean_serial = re.sub(r'[^\w\u4e00-\u9fff\-]', '', prefix_serial) # 生成文件名 file_extension = file.filename.split('.')[-1] if '.' in file.filename else 'jpg' if clean_serial: filename = f"{clean_username}-{code}-{clean_serial}.{file_extension}" else: filename = f"{clean_username}-{code}.{file_extension}" ``` ### 避免重名 ```python # 如果文件已存在,添加时间戳 file_path = os.path.join(upload_dir, filename) if os.path.exists(file_path): import time timestamp = int(time.time()) base_name = filename.rsplit('.', 1)[0] filename = f"{base_name}-{timestamp}.{file_extension}" file_path = os.path.join(upload_dir, filename) ``` --- ## 4️⃣ 保存到服务器 ### 存储路径 **目录**: `backend/uploads/collections/` **完整路径**: `/opt/jiachenlong-backend/uploads/collections/` ### 保存代码 ```python # 创建上传目录 upload_dir = "uploads/collections" os.makedirs(upload_dir, exist_ok=True) # 保存文件 with open(file_path, "wb") as buffer: buffer.write(content) ``` ### 文件权限 - **所有者**: root - **权限**: 644 (rw-r--r--) - **组**: root --- ## 5️⃣ 数据库记录 ### 数据表 **表名**: `collection_images` ### 表结构 ```sql CREATE TABLE collection_images ( id VARCHAR(36) PRIMARY KEY, -- UUID collection_id VARCHAR(36), -- 关联藏品 ID filename VARCHAR(255), -- 文件名 original_name VARCHAR(255), -- 原始文件名 path VARCHAR(500), -- 存储路径 created_at TIMESTAMP DEFAULT NOW() -- 创建时间 ); ``` ### 插入记录 ```python from app.models.models import CollectionImage import uuid image = CollectionImage( id=str(uuid.uuid4()), collection_id=collection_id, filename=filename, original_name=file.filename, path=file_path ) db.add(image) db.commit() db.refresh(image) ``` ### 返回数据 ```python return { "message": "上传成功", "image_id": image.id, "filename": filename } ``` --- ## 6️⃣ 图片访问 ### Nginx 代理配置 **文件**: `/etc/nginx/conf.d/jiachenlong.conf` ```nginx # 图片上传文件代理 location /uploads { proxy_pass http://47.110.37.129:3000/uploads; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; client_max_body_size 20M; } ``` ### 访问 URL 格式 ``` http://8.149.137.26/uploads/collections/admin-0001-J051963351.jpeg ``` ### 后端静态文件服务 **文件**: `backend/app/main.py` ```python # 挂载静态文件目录(图片上传) uploads_dir = "uploads" os.makedirs(uploads_dir, exist_ok=True) app.mount("/uploads", StaticFiles(directory=uploads_dir), name="uploads") ``` --- ## 🔍 OCR 识别流程 ### API 端点 **路由**: `POST /api/ocr/recognize` **文件**: `backend/app/routers/ocr.py` ### 识别步骤 1. **读取图片并转 Base64** ```python image_data = await image.read() image_base64 = base64.b64encode(image_data).decode('utf-8') ``` 2. **调用阿里云 DashScope API** ```python payload = { "model": "qwen-vl-max", "messages": [{ "role": "user", "content": [ {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{image_base64}"}}, {"type": "text", "text": PROFESSIONAL_PROMPT} ] }] } ``` 3. **提取识别结果** ```python def extract_fields(text: str) -> dict: patterns = { 'version': r'✅.*?2.*?发行版别.*?[::]\s*(.+?)(?:\n|$)', 'prefix_serial': r'✅.*?6.*?冠字序号.*?[::]\s*(.+?)(?:\n|$)', 'grading_score': r'✅.*?8.*?评级分数.*?[::]\s*(.+?)(?:\n|$)', # ... 更多字段 } ``` 4. **返回结构化数据** ```python return { "success": True, "text": text_content, "fields": fields } ``` --- ## 📋 完整示例 ### 用户上传流程 1. **用户选择图片** → 前端显示预览 2. **点击上传** → 发送到 `/api/ocr/recognize` 3. **OCR 识别** → 提取藏品信息 4. **填写表单** → 用户确认/修改信息 5. **保存藏品** → 创建藏品记录 6. **上传图片** → 发送到 `/api/collections/upload-image` 7. **保存成功** → 返回图片 URL ### 文件命名示例 **输入**: - 用户名:`admin` - 藏品编号:`0001` - 冠字号:`J051963351` - 原始文件名:`001.JPG` **输出**: - 文件名:`admin-0001-J051963351.JPG` - 路径:`uploads/collections/admin-0001-J051963351.JPG` - URL:`http://8.149.137.26/uploads/collections/admin-0001-J051963351.JPG` --- ## ⚠️ 注意事项 ### 安全限制 1. **文件大小**: 最大 10MB 2. **文件类型**: 仅支持图片(image/*) 3. **认证要求**: 必须登录才能上传 4. **权限控制**: 只能上传到自己的藏品 ### 性能优化 1. **图片压缩**: 建议前端先压缩再上传 2. **CDN 加速**: 生产环境建议使用 CDN 3. **缓存策略**: Nginx 配置静态资源缓存 ### 备份策略 1. **定期备份**: 备份 `uploads/collections/` 目录 2. **数据库备份**: 定期导出 `collection_images` 表 3. **异地备份**: 重要图片建议异地备份 --- ## 🔧 故障排查 ### 图片不显示 1. 检查文件是否存在:`ls -lh /opt/jiachenlong-backend/uploads/collections/` 2. 检查数据库记录:`SELECT * FROM collection_images;` 3. 检查 Nginx 日志:`tail -f /var/log/nginx/error.log` 4. 检查后端日志:`tail -f /tmp/uvicorn.log` ### 上传失败 1. 检查文件大小是否超限 2. 检查文件类型是否正确 3. 检查藏品 ID 是否存在 4. 检查磁盘空间是否充足 --- **最后更新**: 2026-03-16 **维护人员**: 菜鸟小 D 🤖