download属性跨域时被浏览器同源策略静默忽略,仅同源有效;服务端设置content-disposition响应头是最可靠解法,纯前端需fetch+blob fallback但受限于内存和ios safari。

download 属性在跨域时根本不起作用,不是兼容性问题,是浏览器强制拦截
只要 href 和当前页面协议、域名、端口任一不同,download 就会被静默忽略——点击后跳转或预览,不会下载,更不会按你写的文件名保存。这不是 Safari 或 Chrome 的 bug,而是所有现代浏览器(Chrome、Firefox、Edge)统一执行的同源策略。
常见误判场景包括:
- 本地用
file://打开 HTML,相对路径看似能下,上线即崩 -
https://app.com页面里写<a href="https://cdn.example.com/report.pdf" download></a>,实际打开 PDF 而非下载 - 接口地址如
/api/export?id=123与前端不同源,download完全无效
验证是否生效最直接的方法:右键链接 → “另存为”,弹出保存框才说明起效;否则就是被浏览器绕过了。
服务端加 Content-Disposition 是最轻量可靠的跨域解法
如果后端能改响应头,一行 Content-Disposition: attachment; filename="report.csv" 就能让任意来源的链接触发下载,且文件名可控。它不依赖 JS、不关心同源、兼容 IE11+。
但必须注意两个硬性条件:
- 响应头需被 CORS 显式暴露:
Access-Control-Expose-Headers: Content-Disposition - 中文文件名要用
filename*=UTF-8''report%E5%8D%B0%E5%85%B3.csv格式,否则旧版 Safari 可能截断
静态资源(CDN)和动态接口都适用,但纯前端无法绕过这个限制——没有服务端配合,这条路走不通。
fetch + Blob 是唯一可行的纯前端 fallback 方案
当服务端不可控时,只能把文件内容先拉到前端内存,再用 Blob 构造同源临时 URL。关键实操点:
- 必须用
response.blob(),不能用response.text(),否则 PDF/ZIP 会损坏 -
a.download值必须显式指定,不能依赖 URL 路径——接口地址本身不含文件名 - 每次调用
URL.createObjectURL(blob)后,必须立即配对调用URL.revokeObjectURL(url),否则内存持续增长 - 大文件(>50MB)慎用,全量加载进内存可能卡死页面甚至 OOM
iOS Safari 全系不支持脚本触发下载(a.click() 无效),此方案在此平台彻底失效,只能引导用户手动“另存为”。
download 属性值的行为边界:合法字符、空值、中文支持
download 不是“建议名”,而是强制使用的文件名,但它有明确限制:
- 值中不能含
/、\、控制字符等路径分隔符,否则整个属性被静默丢弃 - 空字符串(
download="")会让浏览器尝试从href解析文件名,失败则 fallback 到unknown - 现代 Chrome/Edge 对中文名支持良好;Safari 某些版本会把中文转成
do或乱码,稳妥起见优先用英文+下划线 - 若
href是blob:URL,download是唯一决定保存名的方式,URL 本身不提供文件名
真正容易被忽略的是:这个属性只在同源前提下才有意义;一旦跨域,所有关于文件名的设置都失去上下文——它不是没生效,而是压根没被浏览器读取。











