最简单可靠的PDF嵌入方式是使用<iframe>,现代浏览器原生支持,但需注意同源或CORS配置、移动端Safari限制及避免使用<embed>/<object>。

用 <iframe> 嵌入 PDF 最简单也最可靠
绝大多数现代浏览器(Chrome、Edge、Firefox、Safari)原生支持直接在 <iframe> 中加载 PDF,无需额外插件或转换。只要 PDF 文件可被 HTTP 访问,就能直接显示:
<iframe src="manual.pdf" width="100%" height="600px"></iframe>
注意:src 必须是同源 URL 或已开启 CORS 的跨域 PDF(否则会白屏且控制台报 Blocked a frame with origin ...);height 建议设为固定值或最小高度(如 min-height: 500px),避免内容塌陷。
- PDF 路径必须能被浏览器直接 GET 到(不能是后端重定向后的动态路径,除非返回
Content-Type: application/pdf且带正确 CORS 头) - 移动端 Safari 对
<iframe>内 PDF 渲染有缓存和缩放限制,加scrolling="no"可减少意外滚动条 - 不要用
<embed>或<object>—— 它们在 Firefox 和新版 Chrome 中行为不一致,且容易触发“下载而非预览”
PDF 跨域加载失败时的常见错误和绕过方式
典型报错是控制台出现 Failed to load resource: net::ERR_BLOCKED_BY_CLIENT 或 Cross-Origin Read Blocking (CORB),本质是服务端没返回正确的响应头。不是前端代码能单方面解决的。
- 如果 PDF 在自己服务器上:确保响应头包含
Access-Control-Allow-Origin: *(开发环境)或具体域名(生产) - 如果 PDF 存在第三方 CDN(如 AWS S3):需在存储桶策略中启用 CORS,并显式允许
GET方法和application/pdf类型 - 绝对不要尝试用
fetch + blob URL中转 PDF —— 二进制流容易损坏,且大文件会卡死 UI;URL.createObjectURL(new Blob([res.arrayBuffer()]))在 Safari 中可能无法渲染
需要自定义工具栏或翻页逻辑?别手写,用 pdf.js
原生 <iframe> 只能展示,无法监听页码、添加注释、搜索文本或适配深色模式。pdf.js 是 Mozilla 维护的纯 JS 渲染方案,兼容性好,API 明确。
立即学习“前端免费学习笔记(深入)”;
最小可用示例(CDN 方式):
<canvas id="pdf-canvas"></canvas><br><script src="https://cdn.jsdelivr.net/npm/pdfjs-dist@3.4.120/build/pdf.min.js"></script><br><script><br> pdfjsLib.getDocument('report.pdf').promise.then(pdf => {<br> pdf.getPage(1).then(page => {<br> const viewport = page.getViewport({ scale: 1.5 });<br> const canvas = document.getElementById('pdf-canvas');<br> const ctx = canvas.getContext('2d');<br> canvas.height = viewport.height;<br> canvas.width = viewport.width;<br> page.render({ canvasContext: ctx, viewport });<br> });<br> });<br></script>
-
pdf.js默认不带 UI,要完整工具栏得引入pdf.worker.js和viewer.html,但多数项目只需渲染+跳页,不用全量集成 - 注意版本号:v3.x 开始默认使用 ES modules,若用 script 标签引入,必须用
pdfjsLib全局变量名,不能解构 - 服务端 PDF 若含表单或加密,
pdf.js渲染可能失败且无明确提示,先用pdf.getPage(1)测试是否抛出异常
移动端 Safari 的 PDF 渲染陷阱
iOS 上 <iframe src="xxx.pdf"> 表现极不稳定:可能只显示第一页、无法缩放、点击后跳转到系统预览、甚至触发下载。这不是 bug,是 Apple 对内联 PDF 的主动限制。
- 强制全屏打开:给
<iframe>加style="width:100vw; height:100vh;"并监听touchstart后调用window.open(url, '_blank')是较稳妥的降级方案 - 不要依赖
onload或onerror—— iOS iframe 对 PDF 不触发这些事件,改用定时轮询iframe.contentDocument?.readyState - 如果业务强依赖 PDF 交互(比如签名、高亮),移动端务必用
pdf.js,哪怕体积多 500KB —— 原生方案在这里不可控
PDF 嵌入看着简单,实际卡点全在边界场景:跨域头、移动端策略、大文件流式加载、以及用户突然用 IE11 打开页面——这些地方没日志、没报错、只有白屏,得一个个条件排除。



















