Files
1818web-hoduan/COS_POST_FORM_UPLOAD_FIXED.md
Claude Workbench e3e6f1f29d first commit
2026-02-13 18:18:20 +08:00

313 lines
8.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# COS POST 表单上传 - 完整修复版
## ✅ 问题已修复
已修复 COS POST 表单上传的签名问题:
- ✅ 添加了 `q-key-time` 字段
- ✅ 添加了 `q-sign-time` 字段
- ✅ Policy 中包含必需的签名条件
- ✅ 使用正确的 COS 签名算法
---
## 📋 前端正确的上传代码
### Vue 3 + Element Plus 完整示例
```vue
<template>
<el-upload
:action="uploadAction"
:data="uploadData"
:before-upload="beforeUpload"
:on-success="handleSuccess"
:on-error="handleError"
accept="image/*"
>
<el-button type="primary">上传图片</el-button>
</el-upload>
</template>
<script setup>
import { ref } from 'vue';
import { ElMessage } from 'element-plus';
const uploadAction = ref('');
const uploadData = ref({});
// 上传前获取签名
const beforeUpload = async (file) => {
try {
// 1. 获取 POST 签名
const response = await fetch('/user/oss/post-signature', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + localStorage.getItem('token')
},
body: JSON.stringify({
fileName: file.name,
userId: '123' // 从登录状态获取
})
});
const result = await response.json();
if (result.code !== 200) {
ElMessage.error(result.message);
return false;
}
const data = result.data;
// 2. 设置上传地址
uploadAction.value = data.host;
// 3. 设置表单数据COS 标准字段)
uploadData.value = {
key: data.dir + Date.now() + '_' + file.name, // 文件路径
policy: data.policy, // Policy
'q-sign-algorithm': data['q-sign-algorithm'], // 签名算法
'q-ak': data['q-ak'], // SecretId
'q-key-time': data['q-key-time'], // KeyTime必需
'q-signature': data['q-signature'] // 签名
};
console.log('上传配置:', uploadData.value);
return true;
} catch (error) {
console.error('获取签名失败:', error);
ElMessage.error('获取上传签名失败');
return false;
}
};
const handleSuccess = (response, file) => {
const fileUrl = uploadAction.value + '/' + uploadData.value.key;
ElMessage.success('上传成功');
console.log('文件地址:', fileUrl);
};
const handleError = (error) => {
console.error('上传失败:', error);
ElMessage.error('上传失败');
};
</script>
```
---
### 原生 JavaScript 示例
```javascript
async function uploadFileToCOS(file, userId) {
try {
// 1. 获取 POST 签名
const signResponse = await fetch('/user/oss/post-signature', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + localStorage.getItem('token')
},
body: JSON.stringify({
fileName: file.name,
userId: userId
})
});
const signResult = await signResponse.json();
if (signResult.code !== 200) {
throw new Error(signResult.message);
}
const data = signResult.data;
// 2. 构造表单数据COS 标准字段)
const formData = new FormData();
const fileKey = data.dir + Date.now() + '_' + file.name;
formData.append('key', fileKey); // 文件路径
formData.append('policy', data.policy); // Policy
formData.append('q-sign-algorithm', data['q-sign-algorithm']); // 签名算法
formData.append('q-ak', data['q-ak']); // SecretId
formData.append('q-key-time', data['q-key-time']); // KeyTime必需
formData.append('q-signature', data['q-signature']); // 签名
formData.append('file', file); // 文件(必须最后)
// 3. 上传到 COS
const uploadResponse = await fetch(data.host, {
method: 'POST',
body: formData
});
if (!uploadResponse.ok) {
const errorText = await uploadResponse.text();
console.error('COS 返回错误:', errorText);
throw new Error('上传失败: ' + uploadResponse.status);
}
// 4. 上传成功
const fileUrl = data.host + '/' + fileKey;
console.log('上传成功,文件地址:', fileUrl);
return fileUrl;
} catch (error) {
console.error('上传失败:', error);
throw error;
}
}
// 使用示例
document.getElementById('fileInput').addEventListener('change', async (e) => {
const file = e.target.files[0];
if (file) {
try {
const fileUrl = await uploadFileToCOS(file, '123');
alert('上传成功: ' + fileUrl);
} catch (error) {
alert('上传失败: ' + error.message);
}
}
});
```
---
## 📡 后端返回的签名数据
```json
{
"code": 200,
"message": "POST签名生成成功",
"data": {
"policy": "eyJleHBpcmF0aW9uIjoi...",
"q-sign-algorithm": "sha1",
"q-ak": "AKIDVY1HLBnDZhbHkz0mLhgT3TgePXHNErLC",
"q-key-time": "1733472660;1733476260",
"q-sign-time": "1733472660;1733476260",
"q-signature": "7758dc9a832e9d301dca704cacbf9d9f8172abcd",
"host": "https://oss-1818ai-user-img-1302947942.cos.ap-guangzhou.myqcloud.com",
"dir": "user_img/",
"fileName": "avatar.jpg",
"fileType": "image",
"maxFileSize": 10485760,
"maxFileSizeMB": 10
}
}
```
---
## 🔑 必需的表单字段
前端提交表单时**必须包含**以下字段:
| 字段名 | 说明 | 示例值 |
|--------|------|--------|
| `key` | 文件路径 | `user_img/1733472660_avatar.jpg` |
| `policy` | Base64 编码的 Policy | `eyJleHBpcmF0aW9uIjoi...` |
| `q-sign-algorithm` | 签名算法 | `sha1` |
| `q-ak` | SecretId | `AKIDVY1HLBnDZhbHkz0mLhgT3TgePXHNErLC` |
| `q-key-time` | 密钥有效时间 | `1733472660;1733476260` |
| `q-signature` | 签名 | `7758dc9a832e9d301dca704cacbf9d9f8172abcd` |
| `file` | 文件内容 | (二进制数据,必须最后) |
---
## ⚠️ 常见错误和解决方案
### 1. SignatureDoesNotMatch - q-key-time is required
**错误信息:**
```xml
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>form field q-key-time is required,but not found or empty.</Message>
</Error>
```
**原因:** 表单中缺少 `q-key-time` 字段
**解决:** 确保表单包含:
```javascript
formData.append('q-key-time', data['q-key-time']);
```
---
### 2. InvalidPolicyDocument - q-sign-time is required
**错误信息:**
```xml
<Error>
<Code>InvalidPolicyDocument</Code>
<Message>policy condition q-sign-time is required,but not found.</Message>
</Error>
```
**原因:** Policy 中缺少 `q-sign-time` 条件
**解决:** 后端已修复,重新编译部署即可
---
### 3. SignatureDoesNotMatch - 签名不匹配
**原因:** 签名计算错误或字段值不匹配
**解决:**
1. 确保 `q-key-time``q-sign-time` 的值相同
2. 确保 `key` 字段以 `dir` 开头
3. 确保所有字段值与后端返回的完全一致
---
### 4. CORS 错误
**解决:** 在腾讯云 COS 控制台配置 CORS
- 来源:`*` 或具体域名
- 方法:`GET, POST, PUT, HEAD`
- Allow-Headers`*`
---
## 🧪 测试上传
### 使用 curl 测试
```bash
# 1. 获取签名
curl -X POST http://localhost:8083/user/oss/post-signature \
-H "Content-Type: application/json" \
-d '{"userId":"123","fileName":"test.jpg"}'
# 2. 使用返回的签名上传(替换实际值)
curl -X POST "https://oss-1818ai-user-img-1302947942.cos.ap-guangzhou.myqcloud.com/" \
-F "key=user_img/test.jpg" \
-F "policy=<返回的policy>" \
-F "q-sign-algorithm=sha1" \
-F "q-ak=<返回的q-ak>" \
-F "q-key-time=<返回的q-key-time>" \
-F "q-signature=<返回的q-signature>" \
-F "file=@/path/to/test.jpg"
```
---
## 📚 参考文档
- [腾讯云 COS POST Object 官方文档](https://cloud.tencent.com/document/product/436/14690)
- [COS 请求签名算法](https://cloud.tencent.com/document/product/436/7778)
---
## ✅ 总结
修复后的签名已经完全符合 COS 规范:
- ✅ 包含所有必需字段
- ✅ 签名算法正确
- ✅ Policy 格式正确
- ✅ 前端代码简单明了
**重新编译部署后即可正常使用!**