使用JavaScript上传大文件至S3时的CORS异常求助
问题描述
使用fetch API通过PUT方法结合AWS S3预签名URL上传本地文件时遇到CORS问题:
- 上传30KB左右的小文件时,单次PUT请求可正常完成;
- 上传2MB的大文件时,浏览器先发送OPTIONS预请求(返回200 OK,响应头包含
Access-Control-Allow-Origin: *),但后续的实际PUT请求失败,且PUT请求的响应头中没有Access-Control-Allow-Origin字段。
已在S3存储桶中配置CORS策略,包含"Access-Control-Allow-Origin": "*"。代码如下:
let command = new PutObjectCommand({ Body: "selectedfile", Bucket: "my bucket", Key: "filename" }); let signedurl = await getSignedUrl({ region:"myregion", credentials:{accessKeyId:"mykey",secretAccessKey:"mykey"}}, command, {expiresIn:3600} ); let response = await fetch(signedurl, {method: "PUT", body: "selectedfile"});
解决方案
1. 修正S3 CORS策略配置
S3的CORS策略需要明确允许PUT方法,确保实际请求响应能返回Access-Control-Allow-Origin。更新CORS策略为:
[ { "AllowedHeaders": ["*"], "AllowedMethods": ["PUT", "OPTIONS"], "AllowedOrigins": ["*"], "ExposeHeaders": ["ETag"] } ]
AllowedMethods必须包含PUT和OPTIONS,覆盖预请求与实际请求的方法;ExposeHeaders可选,若需要读取响应中的ETag等字段建议添加;- 保存配置后等待数分钟,让S3策略生效(配置可能存在延迟)。
2. 对齐预签名URL与请求的请求头
大文件上传时,浏览器会自动携带Content-Type等头信息,预签名URL需包含这些头的签名,否则S3会拒绝请求。修改代码如下:
// 替换为实际的File对象 const selectedFile = document.querySelector('input[type="file"]').files[0]; const contentType = selectedFile.type || 'application/octet-stream'; let command = new PutObjectCommand({ Body: selectedFile, Bucket: "my bucket", Key: "filename", ContentType: contentType // 预签名时指定Content-Type }); let signedurl = await getSignedUrl({ region:"myregion", credentials:{accessKeyId:"mykey",secretAccessKey:"mykey"}}, command, {expiresIn:3600} ); let response = await fetch(signedurl, { method: "PUT", body: selectedFile, headers: { 'Content-Type': contentType // 请求时匹配预签名的Content-Type } });
注意:原代码中Body为字符串"selectedfile"是笔误,需替换为实际的File对象。
3. 验证权限与预签名URL有效性
- 确认生成预签名URL的IAM用户拥有
s3:PutObject权限; - 检查预签名URL的有效期(你设置的3600秒正常),避免请求时URL已过期;
- 核对URL中的Bucket、Key拼写是否正确。
4. 改用AWS SDK自动处理大文件上传
对于大文件,浏览器可能触发分块上传,手动预签名URL方式易出问题。建议使用AWS SDK的Upload类,它会自动处理分块、CORS与重试:
import { Upload } from "@aws-sdk/lib-storage"; import { S3Client } from "@aws-sdk/client-s3"; const selectedFile = document.querySelector('input[type="file"]').files[0]; const upload = new Upload({ client: new S3Client({ region:"myregion", credentials:{accessKeyId:"mykey",secretAccessKey:"mykey"} }), params: { Bucket: "my bucket", Key: "filename", Body: selectedFile, ContentType: selectedFile.type } }); // 可选:监听上传进度 upload.on("httpUploadProgress", (progress) => { console.log(`上传进度:${progress.loaded}/${progress.total}`); }); const result = await upload.done();
内容的提问来源于stack exchange,提问作者edward kim
相关产品推荐
相关产品推荐

