444 lines
9.3 KiB
Markdown
444 lines
9.3 KiB
Markdown
# 七牛云直传配置说明
|
||
|
||
## 概述
|
||
|
||
新的上传机制实现了前端直接上传到七牛云,不再通过后端中转,大幅提升大文件上传效率。
|
||
|
||
## 上传流程对比
|
||
|
||
### 旧流程(低效)
|
||
```
|
||
前端 → 后端服务器 → 七牛云
|
||
(中转暂存)
|
||
```
|
||
|
||
问题:
|
||
- 大文件需要先上传到服务器,再由服务器上传到七牛云
|
||
- 占用服务器带宽和磁盘空间
|
||
- 上传时间翻倍
|
||
- 服务器压力大
|
||
|
||
### 新流程(高效)
|
||
```
|
||
前端 → 七牛云(直传)
|
||
后端 → 数据库(仅保存记录)
|
||
```
|
||
|
||
优势:
|
||
- 前端直接上传到七牛云,不经过服务器
|
||
- 节省服务器资源
|
||
- 上传速度快
|
||
- 支持断点续传
|
||
|
||
## 安装依赖
|
||
|
||
### 前端安装七牛云 SDK
|
||
|
||
```bash
|
||
cd platform
|
||
npm install qiniu-js
|
||
```
|
||
|
||
或使用 yarn:
|
||
|
||
```bash
|
||
yarn add qiniu-js
|
||
```
|
||
|
||
## 后端 API
|
||
|
||
### 1. 获取存储配置
|
||
|
||
**接口**: `GET /platform/storage/config`
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {
|
||
"storageType": "qiniu", // 或 "local"
|
||
"qiniuDomain": "http://7colud.yunzer.cn",
|
||
"qiniuRegion": "z0"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2. 获取上传凭证
|
||
|
||
**接口**: `GET /platform/qiniu/token`
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {
|
||
"token": "七牛云上传token",
|
||
"domain": "http://7colud.yunzer.cn",
|
||
"bucket": "your-bucket",
|
||
"region": "z0",
|
||
"keyPrefix": "2026/04/09/1775722615052606500",
|
||
"expires": 1712654400,
|
||
"uploadUrl": "https://up-z0.qiniup.com"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3. 保存文件记录
|
||
|
||
**接口**: `POST /platform/qiniu/save`
|
||
|
||
**请求**:
|
||
```json
|
||
{
|
||
"key": "2026/04/09/1775722615052606500.png",
|
||
"hash": "FhGxwBzoLwO_RGws...",
|
||
"size": 1024000,
|
||
"name": "screenshot.png",
|
||
"mimeType": "image/png",
|
||
"cate": 0
|
||
}
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {
|
||
"url": "http://7colud.yunzer.cn/2026/04/09/1775722615052606500.png",
|
||
"id": 123,
|
||
"name": "screenshot.png",
|
||
"key": "2026/04/09/1775722615052606500.png"
|
||
}
|
||
}
|
||
```
|
||
|
||
## 前端使用
|
||
|
||
### 基础用法
|
||
|
||
```javascript
|
||
import { smartUpload } from '@/utils/qiniuUpload';
|
||
|
||
// 自动选择上传方式(本地或七牛云)
|
||
const result = await smartUpload(file, {
|
||
cate: 0, // 文件分类
|
||
onProgress: (progress) => {
|
||
console.log('上传进度:', progress.percent + '%');
|
||
console.log('已上传:', progress.loaded);
|
||
console.log('总大小:', progress.total);
|
||
},
|
||
});
|
||
|
||
console.log('上传成功:', result);
|
||
// { url: '...', id: 123, name: '...', key: '...' }
|
||
```
|
||
|
||
### 在组件中使用
|
||
|
||
```vue
|
||
<template>
|
||
<el-upload
|
||
:http-request="handleUpload"
|
||
:on-progress="handleProgress"
|
||
>
|
||
<el-button>上传文件</el-button>
|
||
</el-upload>
|
||
<el-progress v-if="uploading" :percentage="uploadPercent" />
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref } from 'vue';
|
||
import { smartUpload } from '@/utils/qiniuUpload';
|
||
import { ElMessage } from 'element-plus';
|
||
|
||
const uploading = ref(false);
|
||
const uploadPercent = ref(0);
|
||
|
||
async function handleUpload(options) {
|
||
const file = options.file?.raw || options.file;
|
||
uploading.value = true;
|
||
|
||
try {
|
||
const result = await smartUpload(file, {
|
||
cate: 0,
|
||
onProgress: (progress) => {
|
||
uploadPercent.value = progress.percent;
|
||
},
|
||
});
|
||
|
||
ElMessage.success('上传成功');
|
||
options.onSuccess?.(result);
|
||
} catch (error) {
|
||
ElMessage.error(error.message || '上传失败');
|
||
options.onError?.(error);
|
||
} finally {
|
||
uploading.value = false;
|
||
}
|
||
}
|
||
</script>
|
||
```
|
||
|
||
### 批量上传
|
||
|
||
```javascript
|
||
import { batchUpload } from '@/utils/qiniuUpload';
|
||
|
||
const files = [file1, file2, file3];
|
||
|
||
const results = await batchUpload(files, {
|
||
cate: 0,
|
||
onFileProgress: (file, progress) => {
|
||
console.log(`${file.name}: ${progress.percent}%`);
|
||
},
|
||
onFileComplete: (file, result) => {
|
||
console.log(`${file.name} 上传成功:`, result);
|
||
},
|
||
onFileError: (file, error) => {
|
||
console.error(`${file.name} 上传失败:`, error);
|
||
},
|
||
});
|
||
|
||
console.log('所有文件上传完成:', results);
|
||
```
|
||
|
||
## 工作原理
|
||
|
||
### 1. 智能选择上传方式
|
||
|
||
`smartUpload` 函数会自动检测后端配置:
|
||
|
||
```javascript
|
||
// 1. 获取存储配置
|
||
const config = await getStorageConfig();
|
||
|
||
// 2. 根据配置选择上传方式
|
||
if (config.storageType === 'qiniu') {
|
||
// 七牛云直传
|
||
return uploadToQiniu(file, options);
|
||
} else {
|
||
// 本地上传(通过后端)
|
||
return uploadToLocal(file, options);
|
||
}
|
||
```
|
||
|
||
### 2. 七牛云直传流程
|
||
|
||
```javascript
|
||
// 1. 获取上传凭证
|
||
const tokenRes = await getQiniuToken();
|
||
const { token, keyPrefix } = tokenRes.data;
|
||
|
||
// 2. 生成文件 key
|
||
const key = `${keyPrefix}.${ext}`;
|
||
|
||
// 3. 使用七牛云 SDK 直接上传
|
||
const observable = qiniu.upload(file, key, token);
|
||
|
||
// 4. 监听上传进度
|
||
observable.subscribe({
|
||
next(res) {
|
||
// 进度回调
|
||
onProgress(res.total.percent);
|
||
},
|
||
complete(res) {
|
||
// 上传完成,保存记录到数据库
|
||
await saveFileRecord({
|
||
key: res.key,
|
||
hash: res.hash,
|
||
size: file.size,
|
||
name: file.name,
|
||
});
|
||
},
|
||
});
|
||
```
|
||
|
||
### 3. 本地上传流程
|
||
|
||
```javascript
|
||
// 通过后端中转(兼容本地存储)
|
||
const formData = new FormData();
|
||
formData.append('file', file);
|
||
|
||
const res = await request({
|
||
url: '/platform/uploadfile',
|
||
method: 'post',
|
||
data: formData,
|
||
onUploadProgress: (e) => {
|
||
onProgress(e.loaded / e.total * 100);
|
||
},
|
||
});
|
||
```
|
||
|
||
## 配置说明
|
||
|
||
### 七牛云区域配置
|
||
|
||
| 区域代码 | 区域名称 | 上传地址 |
|
||
|---------|---------|---------|
|
||
| z0 | 华东 | https://up-z0.qiniup.com |
|
||
| z1 | 华北 | https://up-z1.qiniup.com |
|
||
| z2 | 华南 | https://up-z2.qiniup.com |
|
||
| na0 | 北美 | https://up-na0.qiniup.com |
|
||
| as0 | 新加坡 | https://up-as0.qiniup.com |
|
||
| cn-east-2 | 华东-浙江2 | https://up-cn-east-2.qiniup.com |
|
||
|
||
### 上传策略配置
|
||
|
||
后端生成 token 时的策略:
|
||
|
||
```go
|
||
putPolicy := storage.PutPolicy{
|
||
Scope: cfg.QiniuBucket,
|
||
ReturnBody: `{"key":"$(key)","hash":"$(etag)","size":$(fsize),"mimeType":"$(mimeType)"}`,
|
||
Expires: 3600, // 1小时有效期
|
||
}
|
||
```
|
||
|
||
## 安全性
|
||
|
||
### 1. Token 有效期
|
||
|
||
上传 token 有效期为 1 小时,过期后需要重新获取。
|
||
|
||
### 2. 权限验证
|
||
|
||
- 获取 token 需要登录认证
|
||
- 保存文件记录需要登录认证
|
||
- 文件记录关联到当前用户和租户
|
||
|
||
### 3. 文件去重
|
||
|
||
通过 MD5 检查文件是否已存在,避免重复上传。
|
||
|
||
## 性能优化
|
||
|
||
### 1. 断点续传
|
||
|
||
七牛云 SDK 支持断点续传(大文件自动分片):
|
||
|
||
```javascript
|
||
const config = {
|
||
useCdnDomain: true,
|
||
region: qiniu.region.z0,
|
||
chunkSize: 4, // 分片大小(MB)
|
||
concurrentRequestLimit: 3, // 并发上传数
|
||
};
|
||
|
||
const observable = qiniu.upload(file, key, token, putExtra, config);
|
||
```
|
||
|
||
### 2. CDN 加速
|
||
|
||
启用 CDN 域名加速上传:
|
||
|
||
```javascript
|
||
const config = {
|
||
useCdnDomain: true, // 使用 CDN 加速
|
||
};
|
||
```
|
||
|
||
### 3. 并发上传
|
||
|
||
批量上传时可以控制并发数:
|
||
|
||
```javascript
|
||
// 限制同时上传 3 个文件
|
||
const concurrency = 3;
|
||
const results = [];
|
||
|
||
for (let i = 0; i < files.length; i += concurrency) {
|
||
const batch = files.slice(i, i + concurrency);
|
||
const batchResults = await Promise.all(
|
||
batch.map(file => smartUpload(file, options))
|
||
);
|
||
results.push(...batchResults);
|
||
}
|
||
```
|
||
|
||
## 故障排查
|
||
|
||
### 1. 上传失败:获取 token 失败
|
||
|
||
**错误**: "当前未配置七牛云存储"
|
||
|
||
**解决**:
|
||
- 检查数据库存储配置
|
||
- 确认 `storage_type` 为 `'qiniu'`
|
||
- 确认七牛云配置完整
|
||
|
||
### 2. 上传失败:token 无效
|
||
|
||
**错误**: "401 Unauthorized"
|
||
|
||
**解决**:
|
||
- 检查 AccessKey 和 SecretKey 是否正确
|
||
- 检查 token 是否过期(1小时有效期)
|
||
- 重新获取 token
|
||
|
||
### 3. 上传失败:bucket 不存在
|
||
|
||
**错误**: "no such bucket"
|
||
|
||
**解决**:
|
||
- 检查 bucket 名称是否正确
|
||
- 检查 bucket 是否在对应区域
|
||
- 登录七牛云控制台确认
|
||
|
||
### 4. 保存记录失败
|
||
|
||
**错误**: "保存文件记录失败"
|
||
|
||
**解决**:
|
||
- 检查数据库连接
|
||
- 检查文件信息是否完整
|
||
- 查看后端日志
|
||
|
||
## 迁移指南
|
||
|
||
### 从旧版本迁移
|
||
|
||
1. **安装依赖**:
|
||
```bash
|
||
npm install qiniu-js
|
||
```
|
||
|
||
2. **更新导入**:
|
||
```javascript
|
||
// 旧版本
|
||
import { uploadFile } from '@/api/file';
|
||
|
||
// 新版本
|
||
import { smartUpload } from '@/utils/qiniuUpload';
|
||
```
|
||
|
||
3. **更新上传代码**:
|
||
```javascript
|
||
// 旧版本
|
||
const formData = new FormData();
|
||
formData.append('file', file);
|
||
const res = await uploadFile(formData, { cate: 0 });
|
||
|
||
// 新版本
|
||
const result = await smartUpload(file, { cate: 0 });
|
||
```
|
||
|
||
4. **兼容性**:
|
||
- `smartUpload` 会自动检测存储配置
|
||
- 如果配置为本地存储,会自动使用旧的上传方式
|
||
- 无需修改其他代码
|
||
|
||
## 相关文件
|
||
|
||
### 后端
|
||
- `go/controllers/qiniu_upload.go` - 七牛云上传控制器
|
||
- `go/routers/platform/platform.go` - 路由配置
|
||
- `go/services/storage_service.go` - 存储服务
|
||
|
||
### 前端
|
||
- `platform/src/utils/qiniuUpload.js` - 七牛云上传工具
|
||
- `platform/src/views/platform/softwareupgrade/components/edit.vue` - 软件升级组件(示例)
|
||
|
||
## 更新日期
|
||
|
||
2026-04-09
|