S3直传时签名URL过期了怎么办?
我用预签名URL上传文件到S3,但用户上传大文件时经常提示“SignatureDoesNotMatch”或403错误,估计是URL过期了。试过把过期时间设成1小时,但还是不够用。
有没有办法在前端检测URL是否快过期,或者动态续期?现在用的逻辑是后端生成一次URL就直接给前端用了:
const uploadFile = async (file) => {
const presignedUrl = await fetchPresignedUrl(); // 后端返回的URL
await fetch(presignedUrl, { method: 'PUT', body: file });
};
最靠谱的方案是上分片上传(Multipart Upload)。S3支持把一个大文件拆成多个小片(比如5MB一片)并行上传,每片都有独立的签名,某个分片失败了只需要重传那一片就行,不用整个文件重来。而且S3会在后台合并分片,体验上和普通上传没区别。
具体实现思路:
后端生成预签名URL时,不要只生成一个整文件的URL,而是生成一组分片的签名URL,返回给前端大概这样的结构:
前端拿到后遍历这些URL并行上传,每个上传完拿到ETag存起来,全部传完后再调一次后端接口完成合并。
如果不想自己写分片逻辑,市面上有现成的库可以用,比如aws-sdk-js的MultipartUploader,或者直接用插件。WordPress这边的话,WP Offload Media插件本身就支持分片上传,配置一下S3 bucket和CloudFront就能用,省心。
至于你说的"检测即将过期",这个思路不太对。签名URL本质上是"一次性"的,无法延期。正确的做法是:要么用足够短的超时时间(比如15分钟)+ 错误重试机制;要么直接上分片上传,一劳永逸。
最佳方案:分片上传(Multipart Upload)
这是AWS官方推荐的大文件上传方式,把文件切成多个part并行上传,每个part独立签名、独立校验,某个part失败重传那个就行,不用整个文件重来。而且没有URL过期的问题——只要整个upload流程在7天内完成就行。
简单说下流程:
1. 后端调用S3的createMultipartUpload拿到uploadId
2. 前端把文件切片(通常5MB-100MB一个part),对每个part请求后端生成预签名URL
3. 前端并行上传各个part,全部完成后调用后端完成分片上传
后端用AWS SDK大概是这样:
前端大概逻辑:
如果实在不想改分片,还有个凑合的方案:前端做个定时器,快过期(比如剩余1分钟)时提前重新请求新URL。但说实话,大文件用这个方案就是埋雷,指不定哪个part就超时了。
AWS SDK for JavaScript v3本身就有封装好的upload方法,内部自动做分片,你也可以直接用那个,更省事:
这个Upload类内部自动处理分片、失败重试、并行上传,比自己写省心多了。唯一需要后端做的,就是确保前端有合法的AWS凭证(用Cognito或者临时STS凭证)。
自己选吧,要省心就用SDK的Upload,要完全控制就自己写分片。