超大文件下载怎样设计?

先理解原理和示例,再练习完整口述与追问。

优先让浏览器原生下载:服务端设置 Content-Disposition: attachment 与正确的文件名,前端用 a 链接或预签名 URL 直连对象存储/CDN,不经过 JS 内存。支持 Range 实现断点续传和多线程下载。前端需要处理进度或加工数据时,用 fetch 的流读取,配合 File System Access API 的 showSaveFilePicker 或 StreamSaver 等方案边下载边写盘。

fetch 后调用 response.blob() 会把整个文件读入内存,几 GB 的文件会导致标签页崩溃。浏览器原生下载由下载管理器处理,可暂停、续传,且不受页面关闭影响。需要鉴权时,不要把令牌放在 URL 中长期有效,可由服务端签发短时效的签名 URL,或者先发 POST 请求生成一次性下载链接。

Range 请求头 Range: bytes=0-1048575 返回 206 和对应片段,服务端需声明 Accept-Ranges: bytes;结合 ETag 或 If-Range 确保文件未变化。导出大表格这类需要生成的文件,应由服务端异步生成,生成完成后通知用户下载,而不是让请求长时间挂起。

js
async function saveStream(url, suggestedName) {
  const handle = await window.showSaveFilePicker({ suggestedName })
  const writable = await handle.createWritable()
  const response = await fetch(url)
  if (!response.ok) { await writable.abort(); throw new Error(`HTTP ${response.status}`) }
  await response.body.pipeTo(writable)
}

pipeTo 边读边写,内存占用与文件大小无关。showSaveFilePicker 目前主要在 Chromium 内核浏览器可用,并需要用户手势触发,其他浏览器回退到原生链接下载。

中文文件名需要 filename*=UTF-8'' 编码;跨域的 a 标签 download 属性会被忽略;用 Blob URL 下载后要 URL.revokeObjectURL 释放。

大文件我优先交给浏览器原生下载:服务端设 Content-Disposition attachment,前端用链接或短时效签名 URL 直连 CDN,不经过 JS 内存,服务端支持 Range 实现断点续传。不要用 fetch 后转 blob,会把整个文件读进内存。如果确实要前端处理或显示进度,就读取 fetch 的流,用 showSaveFilePicker 边下边写,并为不支持的浏览器保留原生下载。需要生成的大报表由服务端异步生成后再通知下载。