同域iframe无法访问:加载PDF设置高度遇跨域权限错误
我尝试在iframe中加载PDF并将其高度设置为匹配文档高度,使用的代码如下:
var frame = document.getElementById("preview_frame"); frame.onload = function() { this.style.height = this.contentWindow.document.documentElement.scrollHeight + 'px'; } frame.src = frame.getAttribute("data-url") + "/files/preview/" + target;
文档已加载,但控制台出现如下错误:
Uncaught DOMException: Permission denied to access property "document" on cross-origin object
包含iframe的页面与PDF文档均托管在本地同域下:
- 页面URL:
https://locdev/project-url/public_html - PDF URL:
https://locdev/project-url/public_html/files/preview/file.pdf
我甚至尝试在/project-url/public_html/files/preview/.htaccess中添加:
Header set Access-Control-Allow-Origin "https://locdev"
直接打开PDF文档时能看到该配置已生效,但包含iframe的页面仍报错。我发现如果将PDF替换为图片,代码就能正常运行。请问该如何解决这个问题?
这个问题的核心是:浏览器加载PDF时,会调用内置PDF阅读器插件/渲染器接管iframe,此时iframe的contentWindow不再指向同源文档对象,而是被阅读器的沙箱环境隔离,导致无法访问document属性。图片能正常工作是因为图片直接作为DOM内容渲染,不会触发阅读器接管。
以下是几种可行的解决办法:
1. 用PDF.js渲染PDF(推荐)
放弃浏览器默认PDF阅读器,使用Mozilla的PDF.js库将PDF渲染为HTML元素,这样既能完全控制高度,也不会有跨域问题。
步骤示例:
- 引入PDF.js库
- 用
<div id="pdf-container"></div>替代原iframe作为渲染容器 - 通过PDF.js加载并渲染PDF到容器,渲染完成后设置容器高度
代码片段:
// 配置PDF.js worker路径 pdfjsLib.GlobalWorkerOptions.workerSrc = '//cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js'; async function renderPDF(url) { const container = document.getElementById('pdf-container'); const pdf = await pdfjsLib.getDocument(url).promise; // 逐页渲染PDF for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) { const page = await pdf.getPage(pageNum); const viewport = page.getViewport({ scale: 1 }); const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); canvas.height = viewport.height; canvas.width = viewport.width; await page.render({ canvasContext: context, viewport }).promise; container.appendChild(canvas); } // 设置容器高度匹配内容 container.style.height = container.scrollHeight + 'px'; } // 调用渲染函数 renderPDF(frame.getAttribute("data-url") + "/files/preview/" + target);
2. 间接估算PDF高度(快速方案)
如果不想引入额外库,可以根据PDF页数和单页默认高度估算总高度。A4纸在72dpi下的单页高度约为1123px,总高度为页数 × 1123px,再加少量间距即可。
需要先获取PDF页数,可以通过后端接口读取元数据,或用PDF.js仅获取页数:
async function getPDFPageCount(url) { const pdf = await pdfjsLib.getDocument(url).promise; return pdf.numPages; } async function setIframeHeight() { const frame = document.getElementById("preview_frame"); const pageCount = await getPDFPageCount(frame.getAttribute("data-url") + "/files/preview/" + target); frame.style.height = (pageCount * 1123 + 20) + 'px'; // 20px为额外间距 } setIframeHeight();
3. 禁用浏览器PDF阅读器(不推荐)
通过HTTP头让浏览器直接下载PDF而非内置渲染,但会丢失页面内预览的体验,仅作备选。在.htaccess中添加:
Header set Content-Disposition "inline; filename=file.pdf" Header set Content-Type "application/octet-stream"
内容的提问来源于stack exchange,提问作者php_nub_qq

