346 lines
7.2 KiB
Markdown
346 lines
7.2 KiB
Markdown
|
|
# 小程序头像上传优化说明
|
|||
|
|
|
|||
|
|
## 🔧 问题描述
|
|||
|
|
|
|||
|
|
**旧逻辑**:
|
|||
|
|
- 小程序换头像时直接保存微信临时图片路径
|
|||
|
|
- 临时路径会过期,导致头像无法显示
|
|||
|
|
- 数据库存储的是微信的临时URL
|
|||
|
|
|
|||
|
|
**问题**:
|
|||
|
|
- ❌ 微信临时图片有效期有限(通常几天后失效)
|
|||
|
|
- ❌ 头像无法长期显示
|
|||
|
|
- ❌ 用户体验差
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ✅ 解决方案
|
|||
|
|
|
|||
|
|
**新逻辑**:
|
|||
|
|
1. 用户选择头像后,先上传图片到自己的服务器
|
|||
|
|
2. 服务器保存图片到 `public/assets/avatars/` 目录
|
|||
|
|
3. 返回永久可访问的URL
|
|||
|
|
4. 将永久URL保存到数据库
|
|||
|
|
|
|||
|
|
**优势**:
|
|||
|
|
- ✅ 图片永久保存在自己服务器
|
|||
|
|
- ✅ 头像不会失效
|
|||
|
|
- ✅ 完全可控
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔧 修改的文件
|
|||
|
|
|
|||
|
|
### 1. `miniprogram/pages/my/my.js`
|
|||
|
|
|
|||
|
|
**修改函数**: `onChooseAvatar()`
|
|||
|
|
|
|||
|
|
**旧逻辑**:
|
|||
|
|
```javascript
|
|||
|
|
// 直接使用临时路径
|
|||
|
|
const avatarUrl = e.detail.avatarUrl
|
|||
|
|
userInfo.avatar = avatarUrl
|
|||
|
|
|
|||
|
|
// 保存临时路径到数据库
|
|||
|
|
await app.request('/api/user/update', {
|
|||
|
|
data: { userId: userInfo.id, avatar: avatarUrl }
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**新逻辑**:
|
|||
|
|
```javascript
|
|||
|
|
// 1. 上传到服务器
|
|||
|
|
const uploadRes = await wx.uploadFile({
|
|||
|
|
url: app.globalData.baseUrl + '/api/upload',
|
|||
|
|
filePath: tempAvatarUrl,
|
|||
|
|
name: 'file',
|
|||
|
|
formData: { folder: 'avatars' }
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 2. 获取永久URL
|
|||
|
|
const avatarUrl = app.globalData.baseUrl + uploadRes.data.url
|
|||
|
|
|
|||
|
|
// 3. 保存永久URL到数据库
|
|||
|
|
await app.request('/api/user/update', {
|
|||
|
|
data: { userId: userInfo.id, avatar: avatarUrl }
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2. `miniprogram/pages/settings/settings.js`
|
|||
|
|
|
|||
|
|
**修改函数**: `getWechatAvatar()`
|
|||
|
|
|
|||
|
|
**功能**: 使用 `wx.getUserProfile` 获取微信头像
|
|||
|
|
|
|||
|
|
**修改内容**: 与 `my.js` 相同,先上传图片再保存URL
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📁 服务器存储路径
|
|||
|
|
|
|||
|
|
### 图片保存位置
|
|||
|
|
```
|
|||
|
|
public/assets/avatars/
|
|||
|
|
├── 1738756123456_abc123.jpg
|
|||
|
|
├── 1738756234567_def456.png
|
|||
|
|
└── ...
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 访问URL格式
|
|||
|
|
```
|
|||
|
|
https://soul.quwanzhi.com/assets/avatars/1738756123456_abc123.jpg
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 数据库存储
|
|||
|
|
```javascript
|
|||
|
|
// users 表
|
|||
|
|
{
|
|||
|
|
id: "user_123",
|
|||
|
|
avatar: "https://soul.quwanzhi.com/assets/avatars/1738756123456_abc123.jpg"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔄 上传流程
|
|||
|
|
|
|||
|
|
### 完整流程图
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
用户点击更换头像
|
|||
|
|
↓
|
|||
|
|
微信弹出头像选择器(chooseAvatar / getUserProfile)
|
|||
|
|
↓
|
|||
|
|
获取临时图片路径(tempAvatarUrl)
|
|||
|
|
↓
|
|||
|
|
调用 wx.uploadFile 上传到服务器
|
|||
|
|
↓
|
|||
|
|
服务器保存图片到 public/assets/avatars/
|
|||
|
|
↓
|
|||
|
|
服务器返回永久URL
|
|||
|
|
↓
|
|||
|
|
更新本地 userInfo(app.globalData / storage)
|
|||
|
|
↓
|
|||
|
|
调用 /api/user/update 保存到数据库
|
|||
|
|
↓
|
|||
|
|
完成!
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 代码实现
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// 1. 获取临时头像
|
|||
|
|
const tempAvatarUrl = e.detail.avatarUrl
|
|||
|
|
|
|||
|
|
// 2. 上传到服务器
|
|||
|
|
const uploadRes = await new Promise((resolve, reject) => {
|
|||
|
|
wx.uploadFile({
|
|||
|
|
url: app.globalData.baseUrl + '/api/upload',
|
|||
|
|
filePath: tempAvatarUrl,
|
|||
|
|
name: 'file',
|
|||
|
|
formData: {
|
|||
|
|
folder: 'avatars' // 保存到 avatars 文件夹
|
|||
|
|
},
|
|||
|
|
success: (res) => {
|
|||
|
|
const data = JSON.parse(res.data)
|
|||
|
|
if (data.success) {
|
|||
|
|
resolve(data) // { success: true, data: { url: '/assets/avatars/xxx.jpg' } }
|
|||
|
|
} else {
|
|||
|
|
reject(new Error(data.error))
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
fail: reject
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 3. 拼接完整URL
|
|||
|
|
const avatarUrl = app.globalData.baseUrl + uploadRes.data.url
|
|||
|
|
// 结果: https://soul.quwanzhi.com/assets/avatars/xxx.jpg
|
|||
|
|
|
|||
|
|
// 4. 保存到数据库
|
|||
|
|
await app.request('/api/user/update', {
|
|||
|
|
method: 'POST',
|
|||
|
|
data: { userId: userInfo.id, avatar: avatarUrl }
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔍 错误处理
|
|||
|
|
|
|||
|
|
### 1. 上传失败
|
|||
|
|
```javascript
|
|||
|
|
try {
|
|||
|
|
// ... 上传逻辑
|
|||
|
|
} catch (e) {
|
|||
|
|
wx.showToast({
|
|||
|
|
title: e.message || '上传失败,请重试',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 网络错误
|
|||
|
|
- 自动重试机制(可选)
|
|||
|
|
- 清晰的错误提示
|
|||
|
|
|
|||
|
|
### 3. 文件格式错误
|
|||
|
|
- 服务器会验证文件类型
|
|||
|
|
- 只允许 JPG、PNG、GIF、WebP、SVG
|
|||
|
|
- 文件大小限制 5MB
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📊 服务器API
|
|||
|
|
|
|||
|
|
### `/api/upload` - 图片上传接口
|
|||
|
|
|
|||
|
|
**请求方式**: POST (multipart/form-data)
|
|||
|
|
|
|||
|
|
**参数**:
|
|||
|
|
- `file`: 图片文件
|
|||
|
|
- `folder`: 保存文件夹(如 'avatars')
|
|||
|
|
|
|||
|
|
**返回**:
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"success": true,
|
|||
|
|
"data": {
|
|||
|
|
"url": "/assets/avatars/1738756123456_abc123.jpg",
|
|||
|
|
"fileName": "1738756123456_abc123.jpg",
|
|||
|
|
"size": 45678,
|
|||
|
|
"type": "image/jpeg"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**文件命名规则**:
|
|||
|
|
```javascript
|
|||
|
|
const timestamp = Date.now()
|
|||
|
|
const randomStr = Math.random().toString(36).substring(2, 8)
|
|||
|
|
const fileName = `${timestamp}_${randomStr}.${ext}`
|
|||
|
|
// 例如: 1738756123456_abc123.jpg
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ✅ 测试清单
|
|||
|
|
|
|||
|
|
### 功能测试
|
|||
|
|
- [ ] 在"我的"页面点击头像,选择图片后成功上传
|
|||
|
|
- [ ] 上传后头像立即显示
|
|||
|
|
- [ ] 刷新页面后头像依然正常显示
|
|||
|
|
- [ ] 在设置页面使用"获取微信头像"功能正常
|
|||
|
|
- [ ] 后台管理页面能正确显示用户头像
|
|||
|
|
|
|||
|
|
### 数据验证
|
|||
|
|
- [ ] 数据库 `users.avatar` 字段保存的是完整URL
|
|||
|
|
- [ ] URL格式: `https://soul.quwanzhi.com/assets/avatars/xxx.jpg`
|
|||
|
|
- [ ] 不是微信临时路径(不包含 `weixin` 或 `tmp`)
|
|||
|
|
|
|||
|
|
### 文件验证
|
|||
|
|
- [ ] 服务器 `public/assets/avatars/` 目录存在
|
|||
|
|
- [ ] 上传的图片文件正常保存
|
|||
|
|
- [ ] 文件可通过浏览器直接访问
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🚀 部署步骤
|
|||
|
|
|
|||
|
|
### 1. 确保目录存在
|
|||
|
|
```bash
|
|||
|
|
# 在服务器上创建目录
|
|||
|
|
mkdir -p /www/wwwroot/soul.quwanzhi.com/public/assets/avatars
|
|||
|
|
chmod 755 /www/wwwroot/soul.quwanzhi.com/public/assets/avatars
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 部署代码
|
|||
|
|
```bash
|
|||
|
|
# 本地构建
|
|||
|
|
pnpm build
|
|||
|
|
|
|||
|
|
# 部署到服务器
|
|||
|
|
python devlop.py
|
|||
|
|
|
|||
|
|
# 重启PM2
|
|||
|
|
pm2 restart soul
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 小程序代码上传
|
|||
|
|
- 在微信开发者工具中上传代码
|
|||
|
|
- 提交审核
|
|||
|
|
- 发布新版本
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📝 注意事项
|
|||
|
|
|
|||
|
|
### 1. 兼容性
|
|||
|
|
- 旧版本用户的头像可能还是微信临时路径
|
|||
|
|
- 建议提示用户重新上传头像
|
|||
|
|
|
|||
|
|
### 2. 存储空间
|
|||
|
|
- 每个头像约 50-200KB
|
|||
|
|
- 10000个用户约 0.5-2GB
|
|||
|
|
- 定期清理无用头像(可选)
|
|||
|
|
|
|||
|
|
### 3. CDN优化(可选)
|
|||
|
|
- 如果用户量大,考虑使用CDN加速
|
|||
|
|
- 将 `public/assets/avatars/` 目录同步到CDN
|
|||
|
|
|
|||
|
|
### 4. 图片压缩(可选)
|
|||
|
|
- 可以在上传时自动压缩图片
|
|||
|
|
- 减少存储空间和加载时间
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔄 数据迁移(可选)
|
|||
|
|
|
|||
|
|
如果需要迁移旧数据(微信临时路径 → 永久URL):
|
|||
|
|
|
|||
|
|
### 方案1: 提示用户重新上传
|
|||
|
|
```javascript
|
|||
|
|
// 在小程序中检查头像URL
|
|||
|
|
if (userInfo.avatar && userInfo.avatar.includes('weixin')) {
|
|||
|
|
// 提示用户重新上传头像
|
|||
|
|
wx.showModal({
|
|||
|
|
title: '头像过期',
|
|||
|
|
content: '请重新上传您的头像',
|
|||
|
|
confirmText: '立即上传'
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 方案2: 自动下载并上传(服务器端)
|
|||
|
|
```javascript
|
|||
|
|
// 在服务器端批量处理
|
|||
|
|
// 1. 查询所有微信临时路径的头像
|
|||
|
|
// 2. 下载图片
|
|||
|
|
// 3. 上传到自己服务器
|
|||
|
|
// 4. 更新数据库
|
|||
|
|
// (需要开发专门的迁移脚本)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ✅ 完成状态
|
|||
|
|
|
|||
|
|
- ✅ 修改 `my.js` 的 `onChooseAvatar()` 函数
|
|||
|
|
- ✅ 修改 `settings.js` 的 `getWechatAvatar()` 函数
|
|||
|
|
- ✅ 使用现有的 `/api/upload` 接口
|
|||
|
|
- ✅ 添加错误处理和日志
|
|||
|
|
- ✅ 创建说明文档
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📚 相关文档
|
|||
|
|
|
|||
|
|
- `后台订单显示优化说明.md` - 后台显示头像相关
|
|||
|
|
- `/api/upload` 接口文档
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**优化完成!小程序头像将永久保存在自己的服务器上,不会再失效!**
|