如何使用jQuery print()函数打印在线PDF链接?
解决在线PDF通过iframe打印预览空白的问题
首先,我先帮你排查下代码里的几个明显问题,再针对核心的跨域和加载时机问题给出解决方案:
1. 修复代码语法错误
你的jQuery代码最后少了一组闭合的括号和大括号,这会导致load事件的回调函数根本不会执行!看这里:
$('#print-pdf').load(function () { if (navigator.userAgent.indexOf("Firefox") > 0) { document.getElementById("print-pdf").contentWindow.print(); } else { document.getElementById("print-pdf").contentWindow.document.execCommand('print', false, null); } // 这里缺失了 }); !!! });
先把这个语法错误补上,不然后续的逻辑都跑不起来。
2. 核心问题:跨域限制与PDF加载时机
跨域问题
当你加载的在线PDF和你的项目不在同一个域名下时,浏览器的同源策略会阻止你访问iframe的contentWindow或contentDocument对象,这会导致你调用print()时无法正确获取到PDF内容,从而出现空白预览。
PDF加载时机
即使没有跨域问题,iframe的load事件可能在PDF文件下载完成但还没在浏览器中渲染完成时就触发了,这时候调用打印也会得到空白内容。
3. 可行的解决方案
方案一:修正代码并处理加载延迟(仅适合同域或允许跨域的PDF)
先确保PDF所在服务器设置了CORS允许你的域名访问,然后修改代码,给PDF渲染留一点缓冲时间:
var ifrm = document.getElementById("print-pdf"); ifrm.src = 'http://www2.hawaii.edu/~freeman/courses/phil360/16.%20Myth%20of%20Sisyphus.pdf'; $('#print-pdf').on('load', function () { // 延迟1秒,给PDF足够的渲染时间(时间可根据实际情况调整) setTimeout(function() { var iframeWindow = ifrm.contentWindow || ifrm.contentDocument.defaultView; try { iframeWindow.print(); } catch (e) { // 处理跨域错误 console.error('打印失败,可能是跨域限制:', e); } }, 1000); });
注意:如果服务器没开启CORS,这个方案还是会失败,因为浏览器会阻止跨域访问iframe的内容。
方案二:使用服务器代理(绕过跨域)
如果无法让PDF服务器设置CORS,你可以在自己的服务器上写一个代理接口,把在线PDF的内容转发到同域下,然后让iframe加载这个代理后的URL。比如后端用Node.js写一个简单的代理:
// Node.js 示例(Express框架) app.get('/proxy-pdf', async (req, res) => { const pdfUrl = req.query.url; const response = await fetch(pdfUrl); const buffer = await response.buffer(); res.setHeader('Content-Type', 'application/pdf'); res.send(buffer); });
然后前端iframe的src改成:/proxy-pdf?url=你的在线PDF地址,这样就变成同域请求了,就能正常访问contentWindow并打印。
方案三:使用PDF.js渲染并打印(最稳定的跨域方案)
PDF.js是Mozilla开发的开源库,可以在浏览器中直接渲染PDF内容,完全绕过iframe的跨域限制,还能更好地控制打印流程。步骤如下:
- 引入PDF.js库
- 创建一个canvas元素来渲染PDF
- 渲染完成后调用打印功能
示例代码:
<!-- 引入PDF.js --> <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script> <div id="pdf-container" hidden></div> <script> const pdfUrl = 'http://www2.hawaii.edu/~freeman/courses/phil360/16.%20Myth%20of%20Sisyphus.pdf'; const container = document.getElementById('pdf-container'); // 初始化PDF.js pdfjsLib.getDocument(pdfUrl).promise.then(pdf => { // 渲染所有页面 const renderPages = async () => { for (let i = 1; i <= pdf.numPages; i++) { const page = await pdf.getPage(i); const viewport = page.getViewport({ scale: 1.5 }); const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); canvas.height = viewport.height; canvas.width = viewport.width; container.appendChild(canvas); await page.render({ canvasContext: context, viewport }).promise; } // 所有页面渲染完成后打印 container.style.display = 'block'; window.print(); // 打印后隐藏容器 container.style.display = 'none'; }; renderPages(); }).catch(err => { console.error('加载PDF失败:', err); }); </script>
这个方案不受跨域限制,渲染到canvas后再打印的兼容性和稳定性都更好。
内容的提问来源于stack exchange,提问作者user3140026




