【问题标题】:print a pdf via iframe (cross domain)通过 iframe(跨域)打印 pdf
【发布时间】:2016-11-04 23:37:34
【问题描述】:

我需要打印一个 PDF...但是我收到一个错误

有解决方法吗?我只需要一键打印一个PDF文件

错误:

Uncaught SecurityError: Blocked a frame with origin "https://secure.domain.com" from accessing a frame with origin "https://cdn.domain.com". Protocols, domains, and ports must match.

代码:

var iframe = $('<iframe src="'+url+'" style="display:none"></iframe>').appendTo($('#main')).load(function(){
    iframe.get(0).contentWindow.print();
});

【问题讨论】:

  • 好吧,我相信错误信息非常具有描述性:协议、域和端口必须匹配。 secure.domain.comcdn.domain.com 不同。
  • 必须有解决方法

标签: javascript


【解决方案1】:

您正在处理的错误与跨域保护和同源策略有关。

在您的情况下,如果您将此 iframe 嵌套在另一个我们可以称为代理 iframe 的本地 iframe 中,则可以打印跨域 iframe。

由于代理 iframe 是本地的并且具有相同的来源,因此您可以毫无问题地打印它,它还会打印跨域 iframe。

示例如下:

index.html(容器)

$(function() {
  var url = 'proxy.html'; // We're not loading the PDF but a proxy which will load the PDF in another iframe.

  var iframe = $('<iframe src="' + url + '"></iframe>').appendTo($('#main'));

  iframe.on('load', function(){
    iframe.get(0).contentWindow.print();
  });
});

proxy.html(代理)

<body>
  <iframe src="http://ANOTHER_DOMAIN/PDF_NAME.pdf"></iframe>
</body>

有了这个解决方案,您不再有跨域问题,您可以使用 print() 函数。您唯一需要处理的是将 PDF url 从容器传递到代理的方法以及检测带有 PDF 的 iframe 何时实际加载的方法,但这取决于您使用的解决方案/语言。

【讨论】:

  • 这个答案不起作用(至少在 Chrome 中)。是的,您可以在包装 iframe 上调用 print(),但打印预览是空白的,即使 pdf 内容已加载到内部 iframe 中。
  • 这里#main表示div还是iframe?
【解决方案2】:

--问题--

HiDeo 是对的,这是一个跨域问题。它是CORS 的一部分,这对于网络安全来说是一件好事,但也是一种痛苦。

--哲学--

有很多方法可以解决 CORS,但我相信找到一种适用于大多数情况的解决方案并继续使用它。这创建了更容易推理的代码并保持代码一致,而不是为边缘情况更改和创建代码。这可能会创建一个更难的初始解决方案,但由于您可以在不考虑用例的情况下重用该方法,最终会节省时间。

--回答--

我们团队处理跨域请求问题的方式是完全绕过它。 CORS 仅适用于浏览器。所以解决这个问题的所有情况的最好方法是不要给浏览器合理性。我们让服务器获取文档并将其提供给同一域中的浏览器。

(我是 Angular 的人) 客户会说类似

$http.get('/pdf/x').then(function(){
    //do whatever you want with any cross-domain doument
});

服务器会像你在这里看到的那样HTTP GET Request in Node.js Express

【讨论】:

  • 那么您将如何打印从服务器返回的 pdf 内容?问题是如何在浏览器中打印 pdf,而不是如何检索。
【解决方案3】:

这是一个 CORS 问题。有一个库可以作为 CORS 的替代品,你可以在这里找到它Xdomain CORS alternative Github。它可以无缝绕过 CORS 请求以有效地呈现跨域资源。

它也有一个 Javascript、AngularJS 以及一个 JQuery 包装器。我认为这将为您提供更优雅的解决方案,并且易于集成。试试看吧。

【讨论】:

    【解决方案4】:

    有一个解决方法。

    1. 在您的服务器中创建一个端点以返回外部 url 的 HTML 内容。 (因为您无法从浏览器获取外部内容 - 同源策略

    2. 使用 $.get 从您的 URL 获取外部内容并将其附加到 iframe

    类似的东西:

    HTML:

    <div id="main">
        <iframe id="my-iframe" style="display:none"></iframe>
    </div>
    

    JS:

    $.get('https://secure.domain.com/get-cdndomaincom-url-content', function(response) {
        var iframe = $('#my-iframe')[0],
            iframedoc = iframe.contentDocument || iframe.contentWindow.document;
    
        iframedoc.body.innerHTML = response;
        iframe.contentWindow.print();
    });
    

    get-cdndomaincom-url-content 的 C# 实现:

    Easiest way to read from a URL into a string in .NET

    【讨论】:

    • 但是当我使用 pdf url 时,这会显示编码的 pdf 代码而不是渲染 pdf...有人有想法吗?
    • @Miguel 在您的响应端点 (get-cdndomaincom-url-content) 尝试在响应标头中发送 URL 的 MIME 类型。例如。 application/pdf。参考:stackoverflow.com/a/11945256/826611
    • 或者简单地说;如果是 PDF,您可以尝试直接使用 iframe src 的 URL。
    • 我解决了跨域问题,使用 .php 文件作为来自 url 的代理读取文件......但它有点“丑陋”
    【解决方案5】:

    您不需要代理服务器来解决问题。您可以创建代理 iframe,然后在代理 iframe 内动态创建另一个 iframe。然后附加 onload="print()" 到它。

    类似的东西

      /**
       * Load iframe from cross-origin via proxy iframe
       * and then invokes the print dialog.
       * It is not possible to call window.print() on the target iframe directly
       * because of cross-origin policy.
       * 
       * Downside is that the iframe stays loaded. 
       */
      function printIframe(url) {
        var proxyIframe = document.createElement('iframe');
        var body = document.getElementsByTagName('body')[0];
        body.appendChild(proxyIframe);
        proxyIframe.style.width = '100%';
        proxyIframe.style.height = '100%';
        proxyIframe.style.display = 'none';
    
        var contentWindow = proxyIframe.contentWindow;
        contentWindow.document.open();
        // Set dimensions according to your needs.
        // You may need to calculate the dynamically after the content has loaded
        contentWindow.document.write('<iframe src="' + url + '" onload="print();" width="1000" height="1800" frameborder="0" marginheight="0" marginwidth="0">');
        contentWindow.document.close();
      }
    

    【讨论】:

    • 在处理随机内容时可能很难计算出width="1000" height="1800"。但是,仍然感谢您的解决方案。我现在正在肢解它,看看我能不能到达任何地方。
    【解决方案6】:

    我需要打印 PDF embedded through a data:application/pdf;base64,… iframe,但遇到了同样的跨域问题。

    解决方案是convert the Base64 contents that I had into a blob,然后是use put blob's object URL into the iframe src。之后我就可以打印那个 iframe。

    我知道不鼓励仅提供链接的答案,但将其他人的答案复制到我自己的答案中也感觉不对。

    【讨论】:

    • 最近 iOS 上的 Chrome 无法下载以这种方式打开的文件。您收到“此时无法下载此文件”错误。
    猜你喜欢
    • 2013-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-14
    • 2011-06-14
    • 2014-02-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多