Chrome 65版本多文件下载失效问题求助
解决方案:Chrome 65+批量Blob下载仅最后一个文件的问题
我之前帮好几个开发者排查过一模一样的问题——Chrome 65版本之后,同步循环里触发的多Blob下载会被浏览器合并,只保留最后一个文件的下载请求。这是Chrome在这个版本收紧了对非用户直接连续触发下载的限制,目的是防范恶意批量下载行为。
更优解决方案:异步队列式下载
不需要开新标签页,只需要让每个下载请求有足够的间隔时间,让浏览器能分别处理每个请求的上下文。这里推荐两种实现方式:
方式1:用Async/Await实现串行下载(最直观)
这种方式会逐个处理每个下载,确保上一个下载的上下文被浏览器处理完后再触发下一个,完全不会被合并:
var saveBlobAs = function(blob, filename){ return new Promise((resolve) => { const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); document.body.appendChild(a); a.style = 'display: none'; a.href = url; a.download = filename; a.click(); window.URL.revokeObjectURL(url); // 延迟移除元素,避免DOM操作影响下载上下文 setTimeout(() => { document.body.removeChild(a); resolve(); }, 100); }); }; // 串行处理所有录制文件下载 async function downloadAllRecordings(recordings) { for (const recording of recordings) { await saveBlobAs(recording.blob, recording.fileName); } } // 调用下载函数 downloadAllRecordings(matchedRecordings);
方式2:用延迟触发实现并行(但错开时间)
如果不想用Async/Await,也可以给每个下载请求设置递增的延迟,让它们依次触发:
var saveBlobAs = function(blob, filename){ const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); document.body.appendChild(a); a.style = 'display: none'; a.href = url; a.download = filename; a.click(); window.URL.revokeObjectURL(url); // 清理DOM元素 setTimeout(() => document.body.removeChild(a), 100); }; // 给每个下载设置递增延迟,避免同时触发 for (let i = 0; i < matchedRecordings.length; i++) { const recording = matchedRecordings[i]; setTimeout(() => saveBlobAs(recording.blob, recording.fileName), i * 100); }
为什么之前的方案失效?
Chrome 65+对短时间内连续的、非用户直接交互触发的下载请求做了限制:同步循环里的click事件会被浏览器视为同一批次的请求,为了优化体验(或者防范恶意行为),只会保留最后一个请求的下载上下文。而添加target="_blank"虽然能绕过这个限制,但会触发新标签页,还可能被浏览器的弹窗拦截机制阻止,体验很差。
这两种方案在Chrome 65+和Firefox上都能正常工作,既不会弹出多余标签页,也能完整下载所有文件。
内容的提问来源于stack exchange,提问作者Spider-Paddy
相关产品推荐
相关产品推荐

