jiachenlong/docs/IMAGE_PROCESSING_FLOW.md

417 lines
8.7 KiB
Markdown
Raw Permalink Normal View History

# 图片处理流程文档
**版本**: 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
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`
### 验证流程
```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 🤖