配置CORS后DigitalOcean Spaces预签名链接上传仍报CORS错误
问题描述
已在DigitalOcean Spaces控制台将CORS配置最大化放宽,后端通过以下代码生成预签名URL:
import { S3Client as S3ClientConstructor } from '@aws-sdk/client-s3'; import { PutObjectCommand } from '@aws-sdk/client-s3'; import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; const Spaces = new S3ClientConstructor({ endpoint: 'https://sfo2.digitaloceanspaces.com', region: 'sfo2', forcePathStyle: false, credentials: { accessKeyId: process.env.DO_ACCESS_KEY_ID, secretAccessKey: process.env.DO_SECRET_ACCESS_KEY } }); const uploadUrl = await getSignedUrl( Spaces, new PutObjectCommand({ Bucket: 'my bucket', ACL: 'public-read', // Folder Key: 'dev' }), { // 10min expiresIn: 600 } );
前端使用HTML文件选择器上传的代码如下:
<input className="hidden" onChange={async function upload(e) { setLoading(true); if (!e.target.files?.length) { // User canceled the file browser. return; } try { const uploadUrl = await fetchPresignedUrl(); const file = e.target.files[0]; const formData = new FormData(); formData.append('file', file); const res = await fetch(uploadUrl, { method: 'PUT', body: formData, headers: { 'Content-Type': file.type, 'x-amz-acl': 'public-read' } }); console.log('res.status:', res.status); } finally { setLoading(false); } }} id="upload-button" type="file" />
但上传时仍出现CORS错误:(Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 403,脱敏后的预签名URL为:
https://MY_BUCKET_HERE.sfo2.digitaloceanspaces.com/dev?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=******Faws4_request&X-Amz-Date=20230228T194311Z&X-Amz-Expires=600&X-Amz-Signature=***********&X-Amz-SignedHeaders=host&x-amz-acl=public-read&x-id=PutObject
解决办法
1. 修正前端请求格式
预签名PutObject请求不需要封装FormData,直接将文件作为请求体即可,同时要避免重复设置已包含在签名中的头部:
- 移除
FormData的使用,直接传入file作为body - 删除手动设置的
x-amz-acl头部(预签名URL已携带该参数,重复设置会导致签名验证失败)
修改后的前端代码:
<input className="hidden" onChange={async function upload(e) { setLoading(true); if (!e.target.files?.length) { // User canceled the file browser. return; } try { const uploadUrl = await fetchPresignedUrl(); const file = e.target.files[0]; const res = await fetch(uploadUrl, { method: 'PUT', body: file, headers: { 'Content-Type': file.type } }); console.log('res.status:', res.status); } finally { setLoading(false); } }} id="upload-button" type="file" />
2. 调整预签名URL的Key参数
当前后端代码中Key: 'dev'是目录名,需要指定具体文件名,否则会将文件上传为目录,同时可能影响签名有效性。建议动态生成包含文件名的Key:
// 可从前端传递文件名或后端生成唯一标识 const fileName = 'user-upload.jpg'; const uploadUrl = await getSignedUrl( Spaces, new PutObjectCommand({ Bucket: 'my bucket', ACL: 'public-read', Key: `dev/${fileName}` }), { expiresIn: 600 } );
3. 验证CORS配置完整性
确保Spaces的CORS配置包含以下规则:
- 允许来源:
* - 允许方法:
OPTIONS、PUT - 允许头部:
Content-Type - 最大预检缓存时长:设置为3600等合理值
4. 排查签名验证问题
403状态码通常是签名不匹配导致,此时Spaces返回的响应可能未携带CORS头部,进而触发浏览器的CORS错误。需确认:
- 后端的Access Key和Secret Key拥有PutObject权限
- Bucket名称正确(需小写)
- 预签名时的
endpoint和region与Bucket所在区域一致
内容的提问来源于stack exchange,提问作者Noitidart
相关产品推荐
相关产品推荐

