360 lines
8.3 KiB
Markdown
360 lines
8.3 KiB
Markdown
# 文件上传服务架构说明
|
||
|
||
## 📁 目录结构
|
||
|
||
```
|
||
utils/
|
||
├── IServer/
|
||
│ ├── IUploadService.js # 上传服务接口(抽象基类)
|
||
│ ├── UploadFactory.js # 工厂类(创建上传服务实例)
|
||
│ └── README.md # 本文档
|
||
├── CosUploadService.js # 腾讯云COS上传服务实现
|
||
└── CosUpload.js # 旧版(保留兼容性)
|
||
```
|
||
|
||
## 🎯 设计模式
|
||
|
||
采用 **策略模式 + 工厂模式**:
|
||
|
||
- **策略模式**: 定义统一的上传接口(IUploadService),不同的上传方式实现该接口
|
||
- **工厂模式**: 通过工厂类(UploadFactory)创建具体的上传服务实例
|
||
|
||
## ✨ 优势
|
||
|
||
1. **易扩展**: 添加新的上传方式只需实现接口,无需修改现有代码
|
||
2. **易切换**: 通过工厂类切换不同的上传服务
|
||
3. **解耦合**: 业务代码不依赖具体的上传实现
|
||
4. **易测试**: 可以轻松mock上传服务进行测试
|
||
|
||
## 🚀 快速开始
|
||
|
||
### 方式一:使用工厂创建(推荐)
|
||
|
||
```javascript
|
||
import { AppServer } from '../../modules/api/AppServer.js'
|
||
import UploadFactory from '../../utils/IServer/UploadFactory.js'
|
||
|
||
// 创建上传服务
|
||
const appServer = new AppServer()
|
||
const uploadService = UploadFactory.create(
|
||
'cos', // 服务类型
|
||
async (key) => await appServer.GetCosSign(key), // 签名函数
|
||
{
|
||
// 可选配置
|
||
imageQuality: 80,
|
||
imageMaxSize: 10 * 1024 * 1024,
|
||
videoMaxSize: 100 * 1024 * 1024,
|
||
cosDomain: 'https://miaoyu-1308826010.cos.ap-shanghai.myqcloud.com'
|
||
}
|
||
)
|
||
|
||
// 上传文件
|
||
const result = await uploadService.uploadFile(filePath, 'images')
|
||
if (result.success) {
|
||
console.log('文件URL:', result.data)
|
||
}
|
||
```
|
||
|
||
### 方式二:直接创建实例
|
||
|
||
```javascript
|
||
import CosUploadService from '../../utils/CosUploadService.js'
|
||
|
||
const uploadService = new CosUploadService(
|
||
async (key) => await appServer.GetCosSign(key),
|
||
{ imageQuality: 90 }
|
||
)
|
||
```
|
||
|
||
## 📝 如何添加新的上传服务
|
||
|
||
### 1. 创建上传服务类
|
||
|
||
```javascript
|
||
// utils/MyUploadService.js
|
||
import IUploadService from './IServer/IUploadService.js'
|
||
|
||
class MyUploadService extends IUploadService {
|
||
constructor(config) {
|
||
super()
|
||
this.config = config
|
||
}
|
||
|
||
async uploadFile(filePath, folder = 'images') {
|
||
try {
|
||
// 实现你的上传逻辑
|
||
const fileUrl = await this.doUpload(filePath)
|
||
|
||
return {
|
||
success: true,
|
||
data: fileUrl
|
||
}
|
||
} catch (error) {
|
||
return {
|
||
success: false,
|
||
error: error.message
|
||
}
|
||
}
|
||
}
|
||
|
||
async uploadFiles(filePaths, folder = 'images') {
|
||
// 实现批量上传
|
||
const results = []
|
||
for (const path of filePaths) {
|
||
const result = await this.uploadFile(path, folder)
|
||
if (result.success) {
|
||
results.push(result.data)
|
||
}
|
||
}
|
||
return {
|
||
success: true,
|
||
data: results
|
||
}
|
||
}
|
||
|
||
async getFileInfo(filePath) {
|
||
// 实现获取文件信息
|
||
}
|
||
|
||
validateFile(fileType, fileSize) {
|
||
// 实现文件验证
|
||
return { success: true }
|
||
}
|
||
|
||
async doUpload(filePath) {
|
||
// 你的实际上传逻辑
|
||
}
|
||
}
|
||
|
||
export default MyUploadService
|
||
```
|
||
|
||
### 2. 注册服务
|
||
|
||
```javascript
|
||
import UploadFactory from './IServer/UploadFactory.js'
|
||
import MyUploadService from './MyUploadService.js'
|
||
|
||
// 注册新服务
|
||
UploadFactory.register('myupload', MyUploadService)
|
||
```
|
||
|
||
### 3. 使用新服务
|
||
|
||
```javascript
|
||
const uploadService = UploadFactory.create('myupload', config)
|
||
const result = await uploadService.uploadFile(filePath)
|
||
```
|
||
|
||
## 🔧 配置项说明
|
||
|
||
### CosUploadService 配置
|
||
|
||
| 配置项 | 类型 | 默认值 | 说明 |
|
||
|--------|------|--------|------|
|
||
| imageMaxSize | Number | 10 * 1024 * 1024 | 图片最大大小(字节) |
|
||
| videoMaxSize | Number | 100 * 1024 * 1024 | 视频最大大小(字节) |
|
||
| imageQuality | Number | 80 | 图片压缩质量(0-100) |
|
||
| cosDomain | String | miaoyu的COS域名 | COS访问域名 |
|
||
|
||
## 📖 接口规范
|
||
|
||
### IUploadService 接口方法
|
||
|
||
#### uploadFile(filePath, folder)
|
||
|
||
上传单个文件
|
||
|
||
**参数**:
|
||
- `filePath` (String): 本地文件路径
|
||
- `folder` (String): 文件夹名称,默认 'images'
|
||
|
||
**返回**: Promise<Object>
|
||
```javascript
|
||
{
|
||
success: true/false,
|
||
data: '文件URL', // 成功时
|
||
error: '错误信息' // 失败时
|
||
}
|
||
```
|
||
|
||
#### uploadFiles(filePaths, folder)
|
||
|
||
批量上传文件
|
||
|
||
**参数**:
|
||
- `filePaths` (Array<String>): 文件路径数组
|
||
- `folder` (String): 文件夹名称,默认 'images'
|
||
|
||
**返回**: Promise<Object>
|
||
```javascript
|
||
{
|
||
success: true/false,
|
||
data: ['URL1', 'URL2', ...], // 成功时
|
||
errors: [...], // 部分失败时的错误信息
|
||
error: '错误信息' // 完全失败时
|
||
}
|
||
```
|
||
|
||
## 🎨 使用场景示例
|
||
|
||
### 场景1: 发帖上传图片
|
||
|
||
```javascript
|
||
// 在 post-page.vue 中
|
||
created() {
|
||
const appServer = new AppServer()
|
||
this.uploadService = UploadFactory.create('cos',
|
||
async (key) => await appServer.GetCosSign(key)
|
||
)
|
||
}
|
||
|
||
async uploadImage(filePath) {
|
||
const result = await this.uploadService.uploadFile(filePath, 'images')
|
||
return result.success ? result.data : null
|
||
}
|
||
```
|
||
|
||
### 场景2: 用户认证上传视频
|
||
|
||
```javascript
|
||
async uploadCertVideo(videoPath) {
|
||
const result = await this.uploadService.uploadFile(videoPath, 'videos')
|
||
if (result.success) {
|
||
// 保存视频URL
|
||
this.certificationVideo = result.data
|
||
}
|
||
}
|
||
```
|
||
|
||
### 场景3: 切换上传服务
|
||
|
||
```javascript
|
||
// 从COS切换到服务器上传
|
||
// 只需要修改工厂创建代码,业务代码无需改动
|
||
|
||
// 之前:使用COS
|
||
const uploadService = UploadFactory.create('cos', getSignFn, config)
|
||
|
||
// 现在:使用服务器上传
|
||
const uploadService = UploadFactory.create('server', { apiUrl: '/api/upload' })
|
||
|
||
// 业务代码完全不变
|
||
const result = await uploadService.uploadFile(filePath)
|
||
```
|
||
|
||
## 🔍 已注册的服务类型
|
||
|
||
当前可用的服务类型:
|
||
|
||
```javascript
|
||
UploadFactory.getRegisteredTypes()
|
||
// 返回: ['cos']
|
||
|
||
// 检查是否注册
|
||
UploadFactory.isRegistered('cos') // true
|
||
UploadFactory.isRegistered('aliyun') // false
|
||
```
|
||
|
||
## 📚 未来扩展示例
|
||
|
||
### 阿里云OSS上传服务
|
||
|
||
```javascript
|
||
// utils/AliyunOssUploadService.js
|
||
import IUploadService from './IServer/IUploadService.js'
|
||
|
||
class AliyunOssUploadService extends IUploadService {
|
||
constructor(getSignFunction, config) {
|
||
super()
|
||
this.getSignFunction = getSignFunction
|
||
this.config = config
|
||
}
|
||
|
||
async uploadFile(filePath, folder) {
|
||
// 实现阿里云OSS上传逻辑
|
||
}
|
||
}
|
||
|
||
// 注册
|
||
UploadFactory.register('aliyun', AliyunOssUploadService)
|
||
|
||
// 使用
|
||
const service = UploadFactory.create('aliyun', getSignFn, config)
|
||
```
|
||
|
||
### 服务器直接上传
|
||
|
||
```javascript
|
||
// utils/ServerUploadService.js
|
||
import IUploadService from './IServer/IUploadService.js'
|
||
|
||
class ServerUploadService extends IUploadService {
|
||
constructor(config) {
|
||
super()
|
||
this.apiUrl = config.apiUrl
|
||
}
|
||
|
||
async uploadFile(filePath, folder) {
|
||
// 使用 uni.uploadFile 直接上传到服务器
|
||
return new Promise((resolve) => {
|
||
uni.uploadFile({
|
||
url: this.apiUrl,
|
||
filePath: filePath,
|
||
name: 'file',
|
||
success: (res) => {
|
||
const data = JSON.parse(res.data)
|
||
resolve({
|
||
success: true,
|
||
data: data.fileUrl
|
||
})
|
||
},
|
||
fail: (err) => {
|
||
resolve({
|
||
success: false,
|
||
error: err.errMsg
|
||
})
|
||
}
|
||
})
|
||
})
|
||
}
|
||
}
|
||
|
||
// 注册
|
||
UploadFactory.register('server', ServerUploadService)
|
||
|
||
// 使用
|
||
const service = UploadFactory.create('server', { apiUrl: '/api/upload' })
|
||
```
|
||
|
||
## 💡 最佳实践
|
||
|
||
1. **统一使用工厂创建**: 保持代码风格一致
|
||
2. **配置外部化**: 将上传服务类型和配置放在配置文件中
|
||
3. **错误处理**: 始终检查 `result.success`
|
||
4. **日志记录**: 在关键步骤添加日志便于调试
|
||
|
||
## 🎯 迁移指南
|
||
|
||
### 从旧版 CosUpload 迁移
|
||
|
||
**旧代码**:
|
||
```javascript
|
||
import CosUpload from '../../utils/CosUpload.js'
|
||
const cosUpload = new CosUpload(getSignFn)
|
||
```
|
||
|
||
**新代码**:
|
||
```javascript
|
||
import UploadFactory from '../../utils/IServer/UploadFactory.js'
|
||
const uploadService = UploadFactory.create('cos', getSignFn)
|
||
```
|
||
|
||
## 📞 技术支持
|
||
|
||
如有问题,请参考:
|
||
- `IUploadService.js` - 接口定义
|
||
- `CosUploadService.js` - COS实现参考
|
||
- `UploadFactory.js` - 工厂使用方法
|
||
|