【问题标题】:JSF Streaming PDF on iOS devicesiOS 设备上的 JSF 流式传输 PDF
【发布时间】:2012-02-04 02:30:06
【问题描述】:

我有一个 JSF 页面,它使用支持 bean 将 PDF 文档流式传输到浏览器。这个功能很好用,直到现在在 iOS 设备上测试这个功能时我还没有遇到任何问题。

在 iPad 和 iPod Touch 上的 Safari 上,当 PDF 流获得响应时,它最终只是被粘贴为网页上的不可滚动层。在桌面浏览器中,文件提示保存或打开,并使用 Adob​​e Reader 正确打开。这是一个问题,因为在 iOS 设备上仅显示 PDF 的第一页的一部分,而无法查看其他部分。

我已经通过网络在这些设备上测试了其他 PDF,但它们的行为并不相同。启动 PDF 应用程序或 Safari 加载可滚动预览,这是所需的结果。其他人有这个吗?

我的支持 bean 正在将 PDF 的缓冲输入流写入输出流。我设置了以下响应标头:

response.setContentType ("application/pdf"); 
response.setContentLength(inputLength); 
response.setHeader ("Content-Disposition", "inline;filename=\"" + saveFilename + ".pdf\""); 
response.setHeader ("Cache-Control", "must-revalidate, post-check=0, pre-check=0, public"); 

我尝试将内容配置从内联切换到附件,但这没有帮助。

【问题讨论】:

    标签: ios jsf


    【解决方案1】:

    我想这些设备需要服务器端的Range 支持,以便可以在单独的部分中请求内容。这最终节省了最终不必要的网络带宽并改善了客户端的性能感受。

    如果这些 PDF 是动态生成的,那么最好的办法是将它们写入磁盘文件系统中的临时位置,并将请求重定向到由 servlet 读取它们的 URL。必须拥有物理文件,因为您想使用RandomAccessFile 以便能够返回文件的部分内容。此类需要磁盘上的物理File

    这是一个使用 File#createTempFile() 创建临时文件并将响应重定向到以文件名作为 URL 参数的 servlet 的示例:

    File tempFile = File.createTempFile(saveFilename, ".pdf");
    OutputStream output = new FileOutputStream(tempFile);
    // Now write PDF to output.
    // ...
    
    // Then, redirect to some servlet URL.
    String tempFilename = tempFile.getName();
    externalContext.redirect("/pdf/" + URLEncoder.encode(tempFilename, "UTF-8"));
    

    然后,您需要一个像this one 这样支持范围请求的servlet。您只需修改它,将basePath 的值更改为System.getProperty("java.io.tmpdir")。最后将这个 servlet 映射到 /pdf/* 的 URL 模式上。

    或者,如果您对服务器拥有完全的管理员控制权,那么您还可以创建一个额外的 webapp 上下文,指向服务器磁盘文件系统上某处的绝对路径。例如。 /var/webapp/pdf。以 Tomcat 为例,您可以通过将以下内容添加到 /conf/server.xml 来公开此内容:

    <Context docBase="/var/webapp/pdf" path="/pdf" />
    

    这样/var/webapp/pdf 文件夹的内容就可供http://localhost:8080/context/pdf 使用。

    在上面的代码示例中稍作改动以指定临时文件位置,

    File tempFile = File.createTempFile(saveFilename, ".pdf", "/var/webapp/pdf");
    // ...
    

    您不再需要自定义 servlet。然后,Tomcat 的 DefaultServlet 将完成正确处理 Range 请求的工作。

    最后但同样重要的是,考虑将临时文件存储在会话中并实现HttpSessionListenerHttpSessionAttributeListener,当会话过期时删除临时文件。

    【讨论】:

    • 感谢您发布 BalusC。我尝试了将文件保存到临时位置并使用 servlet 示例的方法,创建了一个映射到 /pdf/ 的 servlet 并再次尝试了请求。在桌面浏览器上,它按我预期的方式处理,提示打开或保存。在 iOS 上的 Safari 上,它的行为与以前一样,将 PDF 分层到现有网页上,即使使用 response.redirect 也是如此。我开始认为这是 JSF 和 Safari 的错误,但我不确定。这些是机密文件,所以我不能只提供文件的直接 URL。
    • 有趣。当您将静态示例 PDF 放在公共网络内容中并直接引用它以便 DefaultServlet 完成其工作时怎么样?
    • 是的,它工作得很好。 iOS 设备打开它并认为它是 Microsoft Word 文档。
    • 你能调试一下这个上的 HTTP 流量吗?发送和接收了哪些标头和正文?
    猜你喜欢
    • 2013-06-29
    • 2014-06-15
    • 2012-01-11
    • 1970-01-01
    • 2012-10-24
    • 2011-11-19
    • 2015-09-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多