download 属性仅对同源 URL 有效,跨域时被浏览器忽略;data: URL 可用,file:// 协议禁用;动态接口需 JS fetch + blob + createObjectURL + click() 实现下载;iOS Safari 不支持脚本触发下载;服务端 Content-Disposition 响应头最可靠。

download 属性只对同源 URL 生效
加了 download 属性却没下载?大概率是 href 指向了跨域资源。浏览器(Chrome/Edge/Firefox)会直接忽略该属性,点击后仍跳转或在线打开 PDF、CSV 等文件。
同源指协议 + 域名 + 端口三者完全一致。例如页面在 https://app.example.com:8080,href 必须也是这个源下的路径,如 /assets/report.pdf 或 ./data.json;https://cdn.example.com/report.pdf(子域不同)、https://api.other.com/export(跨域)均无效。
-
data:URL 是安全的,比如href="data:text/plain;charset=utf-8,hello",download可用 -
file://协议下该属性基本被禁用,不要测试本地双击 HTML 文件 - 服务端返回的
Content-Disposition: attachment头比download更可靠,且不受同源限制
跨域接口返回文件流时不能靠 download 属性
href 指向 /api/export?format=csv 这类动态接口,download 属性纯属摆设——它不触发请求,也不处理响应体,只对静态 URL 起作用。
真正要下载 API 返回的二进制流,必须用 JS 主动拉取并构造可下载的本地链接:
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 用
fetch()请求接口,确保服务端返回Access-Control-Allow-Origin(若需读取Content-Disposition头,还得加Access-Control-Expose-Headers: Content-Disposition) - 调用
response.blob(),别用response.text(),否则中文、图片、Excel 会损坏 - 用
URL.createObjectURL(blob)创建 blob URL,它是同源的,此时a.download才真正生效 - 必须调用
a.click()触发下载;仅插入 DOM 不会自动下载 - 下载完成后立即执行
URL.revokeObjectURL(url),否则内存持续增长
iOS Safari 完全不支持脚本触发下载
哪怕你把 Blob + createObjectURL + click() 写得再标准,在 iOS/iPadOS 的 Safari 上依然无效。它既忽略 download 属性,也拦截非用户手势触发的下载行为。
用户点击后只会打开预览页,无法自动保存到“文件”App。这不是 bug,是 Apple 的明确限制。
- 没有通用前端绕过方案;
FileSaver.js在这里也退化为打开预览 - 如果必须支持 iOS 下载,只能引导用户长按链接 → “下载链接中的文件”(部分机型支持)
- 更稳妥的做法是服务端返回
Content-Disposition: attachment,配合原生<a href="/export">链接,至少保留右键“下载”选项
服务端设置 Content-Disposition 是最稳的强制下载方式
前端 download 是建议,服务端响应头才是权威指令。只要响应中包含 Content-Disposition: attachment; filename="xxx.pdf",无论是否同源、是否用 <a> 标签、是否跨域,浏览器都会弹出下载对话框。
- Node.js(Express):直接用
res.download(path),或手动设头res.set('Content-Disposition', 'attachment; filename="report.pdf"') - Nginx:对静态文件可用
add_header Content-Disposition "attachment";,但无法动态写 filename,需配合map指令或 Lua - PHP:用
header('Content-Disposition: attachment; filename="data.json");+readfile() - 注意:若服务端已设
filename,前端a.download的值通常会被忽略
真正容易被忽略的是服务端与前端路径的配合关系:你写了 download="invoice.pdf",但服务端返回 Content-Type: text/html,浏览器可能拒收或改名。文件类型、响应头、实际内容三者必须一致。

















