Firefox环境下大文件通过HTTP PUT上传Google Storage失败问题求助
问题根因
- Firefox浏览器对大体积长连接请求的处理逻辑和Chromium内核存在差异:默认的跨源PUT请求超时阈值约为300s,连接断开后浏览器会抛出误导性的CORS错误,本质是底层连接中断后的预检请求失败,并非跨源配置问题。
- 单请求全量上传的逻辑容错率极低:大文件上传过程中任何网络波动、浏览器内存回收、连接超时都会直接中断请求,现有续传逻辑仅在请求失败后才拆分剩余内容,无法处理Firefox大请求异常时offset获取失败的场景。
- 请求头配置不符合标准且存在兼容问题:
- Content-Range使用
*通配符的写法不符合HTTP标准,Firefox对该写法的支持存在bug,当文件超过4GB时会出现头信息解析错误,触发跨源校验失败。 - 硬编码Content-Type为
application/octet-stream,和文件实际类型不匹配,会触发GCS等存储服务的额外校验逻辑,提升超时概率。
- Content-Range使用
修复方案
- 放弃单请求全量上传逻辑,改为预切片分块上传:
固定切片大小为100MB~500MB(可根据平均网速调整),提前拆分文件后逐个切片发起PUT请求,每个切片请求超时时间设置为120s以内,单个切片失败仅重传当前切片即可,避免全量重传。
切片处理示例代码:const CHUNK_SIZE = 10 * 1024 * 1024; // 10MB示例,可按需调整 const totalSize = file.size; const chunks = []; for (let i = 0; i < totalSize; i += CHUNK_SIZE) { const end = Math.min(i + CHUNK_SIZE, totalSize); chunks.push({ blob: file.slice(i, end), start: i, end: end - 1 }); } - 修正请求头配置:
- 移除Content-Range的通配符写法,每次请求填写明确的start、end和文件总大小,标准格式为
bytes {start}-{end}/{totalSize}。 - Content-Type设置为文件实际类型,通过
file.type获取,不要硬编码为application/octet-stream。 - 可选择添加
withCredentials: false配置,避免跨源请求附带Cookie导致的额外校验。
修正后的请求示例:
let headers = new HttpHeaders({ 'Content-Type': file.type, 'Content-Range': `bytes ${currentChunk.start}-${currentChunk.end}/${file.size}`, }); const req = new HttpRequest( 'PUT', url, currentChunk.blob, { headers, reportProgress: true, withCredentials: false }, ); return this.http.request(req); - 移除Content-Range的通配符写法,每次请求填写明确的start、end和文件总大小,标准格式为
- 优化错误处理和续传逻辑:
- 监听上传进度事件,每30s主动记录当前已上传的准确offset,不要仅依赖请求失败回调获取续传位置。
- 捕获到CORS错误时,不要直接终止上传,先向GCS发起HEAD请求查询当前文件的已上传偏移量,再从该位置继续续传,GCS的可恢复上传接口原生支持HEAD请求查询进度。
- 调整Angular HttpClient配置:
关闭不必要的全局请求拦截器,避免拦截器对大请求的头信息、请求体做额外处理导致的超时,大文件上传请求的responseType设置为'text'或'blob',避免大响应体解析异常。
内容的提问来源于stack exchange,提问作者Zoidy
相关产品推荐
相关产品推荐

