228 lines
5.9 KiB
Markdown
228 lines
5.9 KiB
Markdown
|
|
# 管理端OSS上传字段名Bug修复详解
|
|||
|
|
|
|||
|
|
## 🐛 问题详细分析
|
|||
|
|
|
|||
|
|
### 错误现象
|
|||
|
|
```xml
|
|||
|
|
<Error>
|
|||
|
|
<Code>NoSuchKey</Code>
|
|||
|
|
<Message>The specified key does not exist.</Message>
|
|||
|
|
<Key>user_img/covers/82D78B6D-B229-0C7B-2567-C023C0386A0A.png</Key>
|
|||
|
|
</Error>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 问题根源
|
|||
|
|
虽然后端成功生成了OSS签名,但前端上传时使用了错误的FormData字段名,导致文件实际上没有上传到OSS。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔍 字段名对照表
|
|||
|
|
|
|||
|
|
### ❌ 错误的字段名(我们文档中的错误示例)
|
|||
|
|
```javascript
|
|||
|
|
// 错误示例 - 不要使用这些字段名
|
|||
|
|
formData.append('OSSAccessKeyId', signature.accessKeyId); // ❌ 错误
|
|||
|
|
formData.append('signature', signature.signature); // ❌ 错误
|
|||
|
|
formData.append('x-oss-signature-version', signature.version); // ❌ 错误
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### ✅ 正确的字段名(OSS POST 签名 V4 要求)
|
|||
|
|
```javascript
|
|||
|
|
// 正确示例 - 必须使用这些字段名
|
|||
|
|
formData.append('key', objectKey); // ✅ 文件路径
|
|||
|
|
formData.append('policy', signature.policy); // ✅ 策略
|
|||
|
|
formData.append('x-oss-credential', signature.x_oss_credential); // ✅ 凭证
|
|||
|
|
formData.append('x-oss-date', signature.x_oss_date); // ✅ 日期
|
|||
|
|
formData.append('x-oss-signature-version', signature.x_oss_signature_version); // ✅ 版本
|
|||
|
|
formData.append('x-oss-signature', signature.x_oss_signature); // ✅ 签名
|
|||
|
|
formData.append('success_action_status', '200'); // ✅ 成功状态
|
|||
|
|
formData.append('file', file); // ✅ 文件
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔧 修复内容
|
|||
|
|
|
|||
|
|
### 1. 修正后端返回字段名
|
|||
|
|
**文件**: `AdminOssServiceImpl.java`
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
// 修复前
|
|||
|
|
response.put("version", "OSS4-HMAC-SHA256");
|
|||
|
|
response.put("signature", signature);
|
|||
|
|
|
|||
|
|
// 修复后
|
|||
|
|
response.put("x_oss_signature_version", "OSS4-HMAC-SHA256");
|
|||
|
|
response.put("x_oss_signature", signature);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 创建测试页面
|
|||
|
|
**文件**: `test_admin_oss_upload.html`
|
|||
|
|
|
|||
|
|
功能特性:
|
|||
|
|
- 🔐 管理员Token验证
|
|||
|
|
- 📁 多种上传目录选择
|
|||
|
|
- 🔄 新版/兼容接口切换
|
|||
|
|
- 📊 实时上传进度
|
|||
|
|
- 🐛 详细调试信息
|
|||
|
|
- ✅ 文件访问测试
|
|||
|
|
|
|||
|
|
### 3. 修正文档示例
|
|||
|
|
更新所有文档中的前端上传代码示例。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🚀 正确的上传流程
|
|||
|
|
|
|||
|
|
### 步骤1: 获取上传签名
|
|||
|
|
```javascript
|
|||
|
|
const response = await fetch('/admin/oss/post-signature', {
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: {
|
|||
|
|
'Authorization': `Bearer ${adminToken}`,
|
|||
|
|
'Content-Type': 'application/json'
|
|||
|
|
},
|
|||
|
|
body: JSON.stringify({
|
|||
|
|
fileName: file.name,
|
|||
|
|
directory: 'covers',
|
|||
|
|
maxSizeMB: 50
|
|||
|
|
})
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const result = await response.json();
|
|||
|
|
const signature = result.data;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 步骤2: 构建FormData(关键步骤)
|
|||
|
|
```javascript
|
|||
|
|
const formData = new FormData();
|
|||
|
|
|
|||
|
|
// 生成唯一文件名避免冲突
|
|||
|
|
const uniqueFileName = `${Date.now()}_${Math.random().toString(36).substring(2)}_${file.name}`;
|
|||
|
|
const objectKey = `${signature.dir}${uniqueFileName}`;
|
|||
|
|
|
|||
|
|
// 按OSS要求添加字段 - 字段名必须准确!
|
|||
|
|
formData.append('key', objectKey);
|
|||
|
|
formData.append('policy', signature.policy);
|
|||
|
|
formData.append('x-oss-credential', signature.x_oss_credential);
|
|||
|
|
formData.append('x-oss-date', signature.x_oss_date);
|
|||
|
|
formData.append('x-oss-signature-version', signature.x_oss_signature_version);
|
|||
|
|
formData.append('x-oss-signature', signature.x_oss_signature);
|
|||
|
|
formData.append('success_action_status', '200');
|
|||
|
|
formData.append('file', file);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 步骤3: 上传到OSS
|
|||
|
|
```javascript
|
|||
|
|
const uploadResponse = await fetch(signature.host, {
|
|||
|
|
method: 'POST',
|
|||
|
|
body: formData
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (uploadResponse.ok) {
|
|||
|
|
const fileUrl = `${signature.host}/${objectKey}`;
|
|||
|
|
console.log('上传成功:', fileUrl);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🧪 测试验证
|
|||
|
|
|
|||
|
|
### 使用测试页面
|
|||
|
|
1. 访问 `/test_admin_oss_upload.html`
|
|||
|
|
2. 输入管理员Token
|
|||
|
|
3. 选择文件和目录
|
|||
|
|
4. 点击"生成上传签名"
|
|||
|
|
5. 点击"上传文件到OSS"
|
|||
|
|
6. 点击"测试文件访问"
|
|||
|
|
|
|||
|
|
### 预期结果
|
|||
|
|
- ✅ 签名生成成功
|
|||
|
|
- ✅ 文件上传到OSS成功
|
|||
|
|
- ✅ 文件URL可正常访问
|
|||
|
|
- ✅ 不再出现`NoSuchKey`错误
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🛡️ 常见问题排查
|
|||
|
|
|
|||
|
|
### 问题1: 仍然提示NoSuchKey
|
|||
|
|
**可能原因**:
|
|||
|
|
- 前端仍在使用错误的字段名
|
|||
|
|
- 文件名包含特殊字符
|
|||
|
|
- OSS权限配置问题
|
|||
|
|
|
|||
|
|
**解决方案**:
|
|||
|
|
```javascript
|
|||
|
|
// 检查FormData字段名是否正确
|
|||
|
|
console.log('FormData字段:');
|
|||
|
|
for (let pair of formData.entries()) {
|
|||
|
|
console.log(pair[0], ':', pair[1]);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 问题2: 签名生成失败
|
|||
|
|
**可能原因**:
|
|||
|
|
- Token无效或过期
|
|||
|
|
- 权限不足
|
|||
|
|
- 文件类型不支持
|
|||
|
|
|
|||
|
|
**解决方案**:
|
|||
|
|
```javascript
|
|||
|
|
// 检查Token和权限
|
|||
|
|
const token = localStorage.getItem('adminToken');
|
|||
|
|
console.log('当前Token:', token);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 问题3: 上传进度卡住
|
|||
|
|
**可能原因**:
|
|||
|
|
- 网络连接问题
|
|||
|
|
- 文件过大
|
|||
|
|
- OSS服务异常
|
|||
|
|
|
|||
|
|
**解决方案**:
|
|||
|
|
```javascript
|
|||
|
|
// 添加超时处理
|
|||
|
|
const controller = new AbortController();
|
|||
|
|
setTimeout(() => controller.abort(), 60000); // 60秒超时
|
|||
|
|
|
|||
|
|
fetch(signature.host, {
|
|||
|
|
method: 'POST',
|
|||
|
|
body: formData,
|
|||
|
|
signal: controller.signal
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📚 相关文档更新
|
|||
|
|
|
|||
|
|
以下文档已同步更新正确的字段名:
|
|||
|
|
- ✅ [API文档](./admin-oss-upload-api.md)
|
|||
|
|
- ✅ [使用示例](./admin-oss-upload-examples.md)
|
|||
|
|
- ✅ [功能总览](./admin-oss-upload-readme.md)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🎯 总结
|
|||
|
|
|
|||
|
|
### ✅ 修复效果
|
|||
|
|
1. **字段名正确**: 使用OSS规范的字段名
|
|||
|
|
2. **上传成功**: 文件能正确上传到OSS
|
|||
|
|
3. **访问正常**: 上传后的文件URL可正常访问
|
|||
|
|
4. **测试工具**: 提供完整的测试页面
|
|||
|
|
|
|||
|
|
### 🚨 重要提醒
|
|||
|
|
1. **字段名必须准确**: OSS对字段名大小写敏感
|
|||
|
|
2. **文件名唯一**: 建议使用时间戳+随机数避免覆盖
|
|||
|
|
3. **错误处理**: 做好网络异常和上传失败的处理
|
|||
|
|
4. **调试信息**: 使用测试页面查看详细的调试信息
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**修复状态**: ✅ 已完成
|
|||
|
|
**测试状态**: ✅ 已验证
|
|||
|
|
**文档状态**: ✅ 已同步
|
|||
|
|
**风险等级**: 低(不影响现有功能)
|