如何通过GCP Storage signed url实现文件分块上传避免内存溢出
关于GCP Storage Signed URL分块上传的实现方案
完全可以实现,GCP原生支持通过Signed URL进行分块上传,不需要依赖任何官方客户端库,纯原生JS就能实现,既可以避免大文件内存溢出,也能很方便的对接进度条展示逻辑。
实现核心原理
GCP对象存储支持**可恢复上传(Resumable Upload)**模式,你可以提前为分块上传场景生成对应的Signed URL,不需要把整个文件读入内存,逐块读取文件切片上传即可。
具体实现步骤
- 第一步:后端预生成可恢复上传的Signed URL,注意生成URL时要指定
x-goog-resumable: start头,签名算法要覆盖这个请求头,拿到的这个URL就是用来初始化分块上传会话的。 - 第二步:前端调用这个初始化URL发POST请求,GCS会返回响应头
Location,这个值就是后续所有分块上传要用的专属会话URL,不需要额外签名,有效期和你最初生成的Signed URL有效期一致。 - 第三步:前端对本地文件做切片,用
File.slice方法按你需要的大小切分,不用把整个文件读入内存,每次只处理当前切片即可。 - 第四步:逐块上传,每个切片请求需要在头里指定
Content-Range标明当前块在整个文件里的字节范围,上传成功后记录当前进度,就能直接实现进度条展示。 - 第五步:所有块上传完成后,发送带完整文件大小的请求确认上传完成即可。
原生JS核心代码示例
// 切片大小,单位字节,GCP要求除最后一块外,每块最小不能小于5MB const CHUNK_SIZE = 5 * 1024 * 1024; async function uploadFile(file, initSignedUrl) { // 1. 初始化上传会话 const initRes = await fetch(initSignedUrl, { method: 'POST', headers: { 'x-goog-resumable': 'start', 'Content-Type': file.type } }); const sessionUrl = initRes.headers.get('Location'); const totalSize = file.size; let uploaded = 0; // 2. 逐块上传 for (let start = 0; start < totalSize; start += CHUNK_SIZE) { const end = Math.min(start + CHUNK_SIZE, totalSize); const chunk = file.slice(start, end); const range = `bytes ${start}-${end - 1}/${totalSize}`; await fetch(sessionUrl, { method: 'PUT', headers: { 'Content-Range': range, 'Content-Type': file.type }, body: chunk }); uploaded += chunk.size; // 直接更新进度条即可,进度值为 uploaded / totalSize * 100 console.log(`上传进度:${((uploaded / totalSize) * 100).toFixed(2)}%`); } console.log('上传完成'); }
注意事项
- 分块大小建议不小于5MB,除了最后一块之外,如果小于这个大小GCS会返回错误。
- 生成初始化Signed URL的时候,必须把
x-goog-resumable: start头纳入签名计算,否则初始化请求会报签名不匹配。 - 会话URL的有效期和你最初生成的Signed URL一致,大文件上传要预留足够的有效期。
内容的提问来源于stack exchange,提问作者Dean Hiller
相关产品推荐
相关产品推荐

