8.7 KiB
8.7 KiB
图片处理流程文档
版本: v1.0.0
更新日期: 2026-03-16
作者: 菜鸟小 D 🤖
📊 完整流程图
用户上传图片
↓
[1] 前端上传组件
↓
[2] 后端接收验证
↓
[3] 文件命名处理
↓
[4] 保存到服务器
↓
[5] 数据库记录
↓
[6] 返回图片 URL
1️⃣ 前端上传组件
上传页面
文件: frontend/src/pages/Add.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
显示逻辑:
<img
src={`/uploads/${img.path}`}
alt={img.originalName}
onError={(e) => {
// 加载失败显示"无图片"占位符
e.target.style.display = 'none';
e.target.parentElement.innerHTML = '<div>无图片</div>';
}}
/>
2️⃣ 后端接收验证
API 端点
文件: backend/app/routers/collections.py
路由: POST /api/collections/upload-image
验证流程
@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)
):
验证步骤
-
验证藏品是否存在
collection = db.query(Collection).filter( Collection.f99_90_id == collection_id ).first() if not collection: raise HTTPException(status_code=404, detail="E00033: 藏品不存在") -
获取用户信息
owner = db.query(User).filter( User.f99_90_id == collection.f99_91_user_id ).first() username = owner.f01_01_name if owner else "unknown" -
获取藏品信息
code = collection.f01_02_code or "0000" prefix_serial = collection.f02_10_prefix_serial or "" -
验证文件类型
if not file.content_type.startswith('image/'): raise HTTPException(status_code=400, detail="E00038: 只能上传图片文件") -
验证文件大小
file_size = len(content) if file_size > 10 * 1024 * 1024: # 10MB raise HTTPException(status_code=400, detail=f"图片大小不能超过 10MB")
3️⃣ 文件命名处理
命名规则
格式: 用户名 - 藏品编号 - 冠字号。扩展名
示例:
admin-0001-J051963351.jpegadmin-0002-J035161361.JPGtestuser-0015.jpeg(无冠字号)
命名代码
# 清理特殊字符,只保留字母、数字、中文、横杠
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}"
避免重名
# 如果文件已存在,添加时间戳
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/
保存代码
# 创建上传目录
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
表结构
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() -- 创建时间
);
插入记录
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)
返回数据
return {
"message": "上传成功",
"image_id": image.id,
"filename": filename
}
6️⃣ 图片访问
Nginx 代理配置
文件: /etc/nginx/conf.d/jiachenlong.conf
# 图片上传文件代理
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
# 挂载静态文件目录(图片上传)
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
识别步骤
-
读取图片并转 Base64
image_data = await image.read() image_base64 = base64.b64encode(image_data).decode('utf-8') -
调用阿里云 DashScope API
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} ] }] } -
提取识别结果
def extract_fields(text: str) -> dict: patterns = { 'version': r'✅.*?2.*?发行版别.*?[::]\s*(.+?)(?:\n|$)', 'prefix_serial': r'✅.*?6.*?冠字序号.*?[::]\s*(.+?)(?:\n|$)', 'grading_score': r'✅.*?8.*?评级分数.*?[::]\s*(.+?)(?:\n|$)', # ... 更多字段 } -
返回结构化数据
return { "success": True, "text": text_content, "fields": fields }
📋 完整示例
用户上传流程
- 用户选择图片 → 前端显示预览
- 点击上传 → 发送到
/api/ocr/recognize - OCR 识别 → 提取藏品信息
- 填写表单 → 用户确认/修改信息
- 保存藏品 → 创建藏品记录
- 上传图片 → 发送到
/api/collections/upload-image - 保存成功 → 返回图片 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
⚠️ 注意事项
安全限制
- 文件大小: 最大 10MB
- 文件类型: 仅支持图片(image/*)
- 认证要求: 必须登录才能上传
- 权限控制: 只能上传到自己的藏品
性能优化
- 图片压缩: 建议前端先压缩再上传
- CDN 加速: 生产环境建议使用 CDN
- 缓存策略: Nginx 配置静态资源缓存
备份策略
- 定期备份: 备份
uploads/collections/目录 - 数据库备份: 定期导出
collection_images表 - 异地备份: 重要图片建议异地备份
🔧 故障排查
图片不显示
- 检查文件是否存在:
ls -lh /opt/jiachenlong-backend/uploads/collections/ - 检查数据库记录:
SELECT * FROM collection_images; - 检查 Nginx 日志:
tail -f /var/log/nginx/error.log - 检查后端日志:
tail -f /tmp/uvicorn.log
上传失败
- 检查文件大小是否超限
- 检查文件类型是否正确
- 检查藏品 ID 是否存在
- 检查磁盘空间是否充足
最后更新: 2026-03-16
维护人员: 菜鸟小 D 🤖