# 七牛云直传配置说明

## 概述

新的上传机制实现了前端直接上传到七牛云，不再通过后端中转，大幅提升大文件上传效率。

## 上传流程对比

### 旧流程（低效）
```
前端 → 后端服务器 → 七牛云
      (中转暂存)
```

问题：
- 大文件需要先上传到服务器，再由服务器上传到七牛云
- 占用服务器带宽和磁盘空间
- 上传时间翻倍
- 服务器压力大

### 新流程（高效）
```
前端 → 七牛云（直传）
后端 → 数据库（仅保存记录）
```

优势：
- 前端直接上传到七牛云，不经过服务器
- 节省服务器资源
- 上传速度快
- 支持断点续传

## 安装依赖

### 前端安装七牛云 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
