如何在浏览器端边接收边下载服务端发送的chunk分块数据?
分块传输边接收边下载实现方案
服务端前提要求
服务端必须返回正确的响应头才能让浏览器识别分块下载:
Transfer-Encoding: chunked:明确指定响应为分块传输模式Content-Disposition: attachment; filename="自定义文件名.后缀":告知浏览器该响应为下载文件,同时指定默认下载文件名- 不要配置
Content-Length响应头,否则分块传输模式不生效 - 跨域场景下需要额外配置
Access-Control-Expose-Headers: Content-Disposition,允许前端读取文件名配置头
前端核心实现逻辑
核心是使用Fetch API读取流式响应,每收到一块数据就更新下载资源,无需等待全部响应加载完成。
代码示例
async function startStreamDownload(apiUrl) { const response = await fetch(apiUrl); // 校验请求合法性 if (!response.ok) { throw new Error(`接口请求失败,状态码:${response.status}`); } const responseReader = response.body.getReader(); const receivedChunks = []; const downloadAnchor = document.createElement('a'); // 提取下载文件名 const dispositionHeader = response.headers.get('Content-Disposition'); const fileName = dispositionHeader ? dispositionHeader.match(/filename="?([^"]+)"?/)[1] : 'default_download.bin'; while (true) { const { done, value } = await responseReader.read(); if (done) break; receivedChunks.push(value); const currentBlob = new Blob(receivedChunks, { type: response.headers.get('Content-Type') || 'application/octet-stream' }); // 释放旧的对象URL避免内存泄漏 if (downloadAnchor.href) { URL.revokeObjectURL(downloadAnchor.href); } downloadAnchor.href = URL.createObjectURL(currentBlob); downloadAnchor.download = fileName; // 首次接收到数据块时直接触发下载 if (receivedChunks.length === 1) { downloadAnchor.click(); } } // 全部数据接收完成后更新最终版本下载资源 const finalBlob = new Blob(receivedChunks, { type: response.headers.get('Content-Type') || 'application/octet-stream' }); URL.revokeObjectURL(downloadAnchor.href); downloadAnchor.href = URL.createObjectURL(finalBlob); // 1分钟后释放最后一次的对象URL setTimeout(() => URL.revokeObjectURL(downloadAnchor.href), 60 * 1000); } // 调用示例 startStreamDownload('/你的分块数据接口地址');
注意事项
- 大文件优化:如果下载文件大小超过1GB,上述方案中存储所有数据块的数组会占用大量内存,建议直接使用流写入文件系统的方案,无需将全量数据暂存在内存中,可大幅降低内存占用。
- 兼容性:该方案支持所有现代浏览器(Chrome 43+、Firefox 65+、Safari 10.1+、Edge 79+),如果需要兼容老旧IE浏览器,可使用XMLHttpRequest的
progress事件模拟实现,不过下载启动时机和流畅度会有所下降。
内容的提问来源于stack exchange,提问作者Phalgun Mittal
相关产品推荐
相关产品推荐

