download属性仅在同源URL下生效,跨域时被浏览器忽略;触发下载需确保href为同源静态路径并显式指定download值,或采用fetch+Blob方案处理跨域资源,最优解是服务端返回Content-Disposition响应头。

download 属性不是万能开关,它只在同源 URL 下生效;跨域链接加了也白加,浏览器直接忽略——这是绝大多数人踩坑的起点。
为什么加了 download 却还是打开 PDF?
根本原因是 href 指向了跨域地址,比如 https://cdn.example.com/report.pdf。浏览器安全策略强制禁用该场景下的 download,点击后退化为跳转或内嵌预览。
- 同源判定严格:协议 + 域名 + 端口三者必须完全一致,
http://a.com和https://a.com不算同源 - 本地开发时用
file://协议打开 HTML 文件,所有相对路径都被视为跨源,download必然失效 -
href为空、为javascript:void(0)或仅含查询参数(如/api/export?id=123)时,浏览器无法推导文件名,可能保存为download或乱码
怎么写才真正触发下载?
确保 href 是可解析的同源静态路径,并显式指定 download 值:
<a href="/files/invoice-2024.pdf" download="发票-2024.pdf">下载发票</a>
- 路径必须真实存在且服务端返回正确 MIME 类型(如
application/pdf),否则部分浏览器会 fallback 到文本渲染 -
download值建议不含非法字符(/、\、:、控制字符),中文名在 Chrome/Edge 中正常,Safari 旧版可能截断 - 不要写
download=""或download="true",这些无效;空字符串会触发 href 路径解析,但不可靠
跨域文件怎么下载?fetch + Blob 是唯一可行前端方案
当资源在 CDN、第三方 API 或无法修改响应头的后端时,必须用 JS 中转二进制流:
立即学习“前端免费学习笔记(深入)”;
async function downloadFromAPI() {
const res = await fetch('https://api.example.com/export?format=csv');
const blob = await res.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = '数据导出.csv';
a.click();
URL.revokeObjectURL(url);
}
- 务必调用
res.blob(),不是res.text(),否则二进制文件(如 Excel、ZIP)会损坏 - 目标接口需支持 CORS,至少返回
Access-Control-Allow-Origin: *;否则 fetch 直接失败 - 大文件(>50MB)慎用此法,Blob 占内存,可能触发 OOM;应交由服务端生成直链或代理
- IE 不支持
fetch和Blob,需降级为XMLHttpRequest+responseType: 'blob'
比 download 更可靠的方案:服务端设 Content-Disposition
如果后端可控,优先让服务端返回 Content-Disposition: attachment; filename="xxx.pdf" 响应头。它不依赖同源,也不需要前端 JS,连 <a href="/export"> 这种无 download 的普通链接都能强制下载。
- Node.js Express 可直接用
res.download(path) - Nginx 静态文件可配
add_header Content-Disposition "attachment";,但无法动态设文件名 - 若服务端返回的是
text/plain或application/json,即使加了download,浏览器仍可能渲染;而Content-Disposition会覆盖该行为
真正容易被忽略的是:同源判断和 CORS 配置不是“试试看”,而是硬性门槛;download 是客户端提示,Content-Disposition 才是服务端指令——两者配合最稳,单靠前端永远有边界。



















