如何利用getSignedUrl从Chrome浏览器上传大文件至Google Cloud Storage?
嘿,这个问题我太熟了!用Google Cloud Storage的预签名(getSignedUrl)链接在Chrome这类浏览器上传大文件,其实用原生的Web API就能轻松搞定,还能实现可靠的分块上传避免中断。下面一步步给你讲清楚:
基础单文件上传(适合中等大小文件)
如果你的文件不算特别大(比如几十MB以内),直接用Fetch API就能快速完成上传:
- 先通过页面的文件选择器获取要上传的文件对象:
// 假设页面上有个id为"file-upload"的<input type="file"> const fileInput = document.getElementById('file-upload'); const targetFile = fileInput.files[0];
- 调用Fetch发送PUT请求到预签名URL,重点要保证请求头的Content-Type和生成预签名URL时指定的完全一致:
async function uploadSingleFile(signedUrl, file) { try { const uploadResponse = await fetch(signedUrl, { method: 'PUT', body: file, headers: { 'Content-Type': file.type // 必须和生成getSignedUrl时的Content-Type匹配! } }); if (uploadResponse.ok) { console.log('文件上传成功!'); // 这里可以添加上传成功后的页面逻辑,比如提示用户 } else { console.error(`上传失败,状态码:${uploadResponse.status},信息:${uploadResponse.statusText}`); } } catch (error) { console.error('上传请求出错:', error); } } // 调用方法:传入你的预签名URL和选中的文件 uploadSingleFile('你的预签名URL', targetFile);
大文件分块上传(推荐,应对超大型文件)
对于几百MB甚至GB级的大文件,一次性上传很容易因为网络波动、超时导致失败,这时候用分块上传更可靠。GCS支持分块上传,每个块至少5MB(最后一块可以更小),步骤如下:
- 定义分块大小,比如设置为10MB(可根据网络情况调整):
const CHUNK_SIZE = 10 * 1024 * 1024; // 10MB const totalChunks = Math.ceil(targetFile.size / CHUNK_SIZE);
- 循环上传每个分块,通过
Content-Range头告诉GCS当前块的范围,GCS会自动合并所有块:
async function uploadLargeFile(signedUrl, file) { const chunkSize = 10 * 1024 * 1024; const totalChunks = Math.ceil(file.size / chunkSize); for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) { const startByte = chunkIndex * chunkSize; // GCS的Content-Range是闭区间,所以endByte要减1 const endByte = Math.min(startByte + chunkSize, file.size) - 1; const currentChunk = file.slice(startByte, endByte + 1); try { const chunkResponse = await fetch(signedUrl, { method: 'PUT', body: currentChunk, headers: { 'Content-Type': file.type, 'Content-Range': `bytes ${startByte}-${endByte}/${file.size}` } }); if (chunkResponse.status === 308) { // 308是正常的续传状态码,表示当前块上传成功,继续传下一块 console.log(`第${chunkIndex + 1}/${totalChunks}块上传完成`); } else if (chunkResponse.ok) { // 最后一块上传完成,整个文件合并成功 console.log('所有块上传完成,文件已成功保存到GCS!'); break; } else { throw new Error(`第${chunkIndex + 1}块上传失败:${chunkResponse.statusText}`); } } catch (error) { console.error('分块上传出错:', error); // 这里可以添加重试逻辑,比如针对失败的块重新上传几次 return; } } } // 调用分块上传方法 uploadLargeFile('你的预签名URL', targetFile);
关键注意事项
这些细节一定要注意,不然很容易踩坑:
- 预签名URL有效期:大文件上传耗时久,生成URL时别设太短的过期时间(比如至少1小时以上),避免上传到一半URL失效。
- Content-Type严格匹配:生成预签名URL时指定的Content-Type,和上传请求头里的必须完全一致,否则会返回403/400错误。
- 跨域配置(CORS):如果你的网页域名和GCS域名跨域,必须给存储桶配置CORS规则,允许你的域名发起PUT请求,并且允许
Content-Type、Content-Range这些请求头。比如用gsutil配置的规则示例:[ { "origin": ["https://你的网页域名.com"], "method": ["PUT"], "responseHeader": ["Content-Type", "Content-Range"], "maxAgeSeconds": 3600 } ] - 上传进度监听:如果要给用户展示上传进度,可以用XMLHttpRequest的
progress事件,或者给Fetch搭配ReadableStream实现。比如XMLHttpRequest的示例:function uploadWithProgress(signedUrl, file) { const xhr = new XMLHttpRequest(); xhr.open('PUT', signedUrl); xhr.setRequestHeader('Content-Type', file.type); // 监听上传进度 xhr.upload.addEventListener('progress', (event) => { if (event.lengthComputable) { const progressPercent = (event.loaded / event.total) * 100; console.log(`上传进度:${progressPercent.toFixed(2)}%`); // 这里可以更新页面上的进度条DOM } }); xhr.onload = () => { xhr.status === 200 ? console.log('上传成功!') : console.error('上传失败:', xhr.statusText); }; xhr.onerror = () => console.error('上传请求出错'); xhr.send(file); }
内容的提问来源于stack exchange,提问作者Leo
相关产品推荐
相关产品推荐

