问题背景
在我们的项目中,原本采用的文件上传方案是将文件先上传到应用服务器,再由服务器转发至华为云OBS。这种架构在实际运行中暴露了两个关键问题:
- 上传速度严重受限:服务器的带宽成为瓶颈(特别是100MB以上的大文件)
- 服务器压力过大:频繁出现负载过载告警
为解决这些痛点,我们决定改为前端直传OBS方案。技术流程如下:
问题一:跨域策略配置难题
问题描述
在调试过程中,前端控制台频繁出现CORS跨域错误,尤其在以下两种场景:
- 本地开发时:浏览器提示
Access-Control-Allow-Origin
缺失 - 生产环境上传时:OBS返回
403 Forbidden
并附带CORS拒绝信息
问题定位
通过分析浏览器网络请求和华为云CORS配置文档(参见官方文档)发现:
- OBS桶默认拒绝所有跨域请求
- 上传请求中包含
Content-MD5
头,被安全策略拦截 - Content-MD5被视为敏感头,需要显式配置
- Nginx反向代理未正确透传必要头信息
解决方案
本地开发配置(vue.config.js)::
// vue.config.js
module.exports = {
devServer: {
proxy: {
'/base-api': {
target: 'https://my-obs-endpoint',
changeOrigin: true,
pathRewrite: { '^/base-api': '' }
}
}
}
}
生产环境解决方案:
- OBS桶CORS配置:
[
{
"AllowedOrigin": ["https://your-domain.com"],
"AllowedMethod": ["PUT", "GET"],
"AllowedHeader": ["*"], // 华为云要求必须配置为*
"ExposeHeader": ["ETag", "x-obs-request-id"],
"MaxAgeSeconds": 300
}
]
- Nginx关键配置:
location / {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Headers'
'Content-MD5, Content-Type, Authorization';
}
扩展知识点
- CORS预检机制:浏览器对非简单请求会先发OPTIONS请求验证权限
- 华为云特殊要求:必须配置
AllowedHeader: *
才能放行Content-MD5等自定义头 - 暴露头信息:需显式配置ETag,否则无法获取文件校验信息
- 调试技巧:使用
curl -v -X OPTIONS https://obs-url
预检请求验证配置
问题二:Content-MD5校验异常
问题描述
上传时报错:
403 Forbidden - InvalidDigest: The Content-MD5 you specified was invalid
即使MD5值看似正确,OBS仍拒绝请求。
问题定位
经过测试对比发现:
- 比对华为云文档要求:
- 要求格式:
Content-MD5: base64(md5Bytes)
- 错误认知:直接传递hex格式md5会导致校验失败
- 要求格式:
- 代码排查发现:
- 原有
crypto
库生成的是hex字符串 - OBS要求Base64编码的字节数组,而非hex转换结果
- 原有
解决方案
优化后的MD5计算方案:
import SparkMD5 from 'spark-md5';
async function calculateObsMD5(file) {
const spark = new SparkMD5.ArrayBuffer();
const chunkSize = 5 * 1024 * 1024; // 5MB分片
let offset = 0;
while (offset < file.size) {
const chunk = file.slice(offset, offset + chunkSize);
const buffer = await chunk.arrayBuffer();
spark.append(buffer);
offset += chunkSize;
}
// 关键转换:十六进制 → 字节数组 → Base64
const hexHash = spark.end();
const rawHash = new Uint8Array(32);
for (let i = 0; i < 32; i += 2) {
rawHash[i/2] = parseInt(hexHash.substr(i, 2), 16);
}
// 浏览器安全转换Base64
return btoa(String.fromCharCode(...rawHash));
}
// 上传请求使用示例
const md5 = await calculateObsMD5(file);
fetch(signedUrl, {
method: 'PUT',
headers: {
'Content-Type': contentType,
'Content-MD5': md5 // 形如:MD5:4XvB3tbNTN+tIEVa0/fGaQ==
},
body: file
});
扩展知识点
- MD5的三种表达形式:
格式 示例 适用场景 HEX e4b0c44298fc1c14...
通用校验 Base64 5L2g5aW977yM5Lit...
HTTP内容校验 ByteArray [228, 176, 196...]
二进制协议
问题三:历史数据导致的404错误
问题描述
测试发现bug:上传文件时偶发OBS返回404 Not Found
错误,但相同文件在测试环境可正常上传。
问题定位
通过日志比对和版本分析发现:
- 历史版本生成的文件路径格式为:
{version1}/{userid}/{filename}
- 新版本统一使用:
{version2}/{date}/{uuid}.ext
- 后端系统存在新老路径兼容处理
解决方案:
- 数据迁移:
# 使用obsutil迁移历史数据
obsutil cp obs://old-bucket/projectA/ obs://new-bucket/tenant01/projectA/ -r
- 接口兼容:
…
方案实施效果与总结
性能对比数据
指标 | 旧方案(服务器中转) | 新方案(直传OBS) | 提升幅度 |
---|---|---|---|
100MB文件上传 | 12.8s | 3.2s | 300% ↑ |
服务器CPU负载 | 峰值85% | 稳定在30% | 65% ↓ |
失败率(网络波动) | 14.2% | 2.1% | 85% ↓ |
关键技术总结
-
直传架构优势:
- 省去服务器中转环节,降低系统复杂性
- 直接利用华为云BGP网络提升传输质量
- 支持客户端断点续传(Range PUT)
-
安全关键点:
- 临时URL有效期控制在10分钟内
- 敏感操作使用HTTPS+内容校验双重保障
- ACL权限最小化原则(只赋予PutObject权限)
-
扩展能力:
该方案上线后,不仅大幅提升了上传性能,还降低了服务器负载。后续可在此基础上实现分片上传和秒传功能,进一步完善大文件传输体验。
经验提示:云存储服务各厂商实现细节差异较大,建议重点阅读华为云OBS RESTful API文档,特别关注请求头要求和错误码规范(ps: 大厂文档写的一言难尽,反复看反复测试)。