【问题标题】:JSF streamed download on android device returns .htm fileandroid 设备上的 JSF 流式下载返回 .htm 文件
【发布时间】:2012-03-28 20:52:34
【问题描述】:

我目前在 Android 设备上的网页中遇到了一个奇怪的问题。

我想要做的是允许用户将 pdf 文件下载到他的移动设备上。因此,我在here 描述的设置中提供了一个下载按钮。

只要我使用我的桌面浏览器 *Mozilla Firefox 10.** 一切正常,但只要我更改为我的移动设备(SGS II,Android 版本 2.3.5)下载结果取决于我使用的浏览器应用程序。

Mozilla 和 Opera 移动版:
两者似乎都能正确下载文件。

任何其他浏览器应用程序(内置、Dolphin HD、...):
下载一个名为 <filename>.pdf<filename>.htm 的文件,它们都代表一个 .htm 文件,显示页面的 html 源代码。

我的尝试:

  • 使用了 PrimeFaces 库中的 StreamedContent 方法

    public StreamedContent getFile() {
        // prepare file for download
        // reference webDAV directory and get file as stream
        this.file = new Helper().getWebDavFile(customerId, fileName);
    
        return file;
    }
    
  • here 所述,手动将文件流式传输到页面。 (感谢 BalusC)

    public void download() throws IOException {
    
        byte[] is = new Helper().getWebDavFileManually(customerId, fileName);
    
        FacesContext fc = FacesContext.getCurrentInstance();
        ExternalContext ec = fc.getExternalContext();
    
        ec.responseReset(); 
        ec.setResponseContentType("application/pdf");  
        ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + fileName.toUpperCase() + "\""); 
    
        OutputStream output = ec.getResponseOutputStream();
        output.write(is);
    
        fc.responseComplete(); 
    }  
    
  • <a href=""> 设置为文件的本地副本。
    (我目前使用的是<p:commandButton>,所以我必须使用执行重定向而不是返回字符串的方法,但它可以双向工作)

    public void goToLink() throws IOException {
    
        // get WebDAV file and save temporarily
        byte[] b = new Helper().getWebDavFileManually(customerId, fileName);
        String path = FacesContext.getCurrentInstance().getExternalContext().getRealPath("/") + fileName;
        File f = new File(path);
        try {
            FileOutputStream fos = new FileOutputStream(f);
            fos.write(b);
            link = "http://someurl/somepage/" + fileName;
        } 
        catch (FileNotFoundException e) {
            e.printStackTrace();
        } 
        catch (IOException e) {
            e.printStackTrace();
        }
    
        // use link
        ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
        ec.redirect(link);
    }
    

    即使在我的 android 设备上,最后的方法也能正常工作,但如果我可以避免它,我不想走这条路,因为文件是从 WebDAV 流式传输的,我必须保存每个文件到服务器。这会产生更多的 IO 负载并迫使我手动清理。

Helper().getWebDavFileHelper().getWebDavFileManually 方法要么返回 PrimeFaces 使用的 DefaultStreamedConten,要么返回 byte[] 用于我自己的方法。

到目前为止我所知道的:

不幸的是,这不是我的问题的解决方案 :)。
在使用 google 几个小时后,我发现可能存在 double-http-post-request。这将导致 androids 内部下载管理器(用于文件下载损坏的情况)发送一个额外的后请求,其中状态丢失。

正如this blog 中所述(参见GET、POST、REST [UPDATE 20120208] 部分),有人面临同样的问题。我已经尝试了此博客中提到的所有方法,但没有成功。
在这个forum 上,有人用 WireShark 分析了相同的行为并得出了几乎相同的结论。
我没有找到更多的资源,所以我被困在了这里。

我还在 PrimeFaces forum 上发帖,只是为了确保没有任何关于 <p:fileDownload> 组件的已知问题。

我想知道的:

我错过了什么吗?
是否有可能从 Android 设备上的 JSF(http-post 操作)网页下载流文件?

任何帮助/建议/信息将不胜感激!
提前致谢!

【问题讨论】:

  • 这是一个讨厌的问题。 POST 真的是强制性的吗?看起来这正是罪魁祸首。您还可以对 JSF 托管 bean 执行 GET 操作,尽管我宁愿为此使用一个简单的 servlet。
  • @BalusC 你好 :)。不,POST 不是真的强制性的。您能否详细说明您的建议。我对 JSF 很陌生,我认为我的视图参数(请参阅 linked 帖子,您已经帮助过我)足以执行 GET。我也对 servlet 方法感兴趣。我将有一个look 看看我是否可以帮助自己,但您的任何著名的启动示例将不胜感激:)。谢谢你,干杯!

标签: jakarta-ee jsf-2 download http-post android


【解决方案1】:

好的,在我遇到了一些其他问题之后,我终于有时间回过头来解决这个问题了。

在使用谷歌花费了几个小时之后,我没有得到任何新线索,正如我的问题中已经提到的那样。

这仍然是一些安卓浏览器无法处理 POST 请求并返回相应文件的事实。

因此,我选择尝试使用 servlet-approach(如 cmets 中所述并描述为 here)并构建我自己的 http-GET-request。

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    ...
    // Initialize servlet response 
    response.reset();
    response.setBufferSize(DEFAULT_BUFFER_SIZE);
    response.setContentType(mime);
    response.setHeader("Content-Length", String.valueOf(data.length));
    response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");

    // write to response
    BufferedOutputStream output = null;
    try {
        output = new BufferedOutputStream(response.getOutputStream());
        output.write(data);
    }
    finally {
        output.close();
    }

瞧:无论使用哪种浏览器,下载都可以在任何安卓设备上运行!

再次感谢@BalusC 为我指明了正确的方向。

如果我有时间我也会尝试 JSF-GET 方法,但起初我对此很满意。

如果有人面临同样的问题或能够提出其他解决方案,我将不胜感激!

这对我有帮助,我会玩得很开心:D!

【讨论】:

    【解决方案2】:

    有同样的问题。我使用来自 primefaces.org 的示例代码通过移动设备下载 pdf。它“在我的机器上”运行良好,但在 iPad Safari 或 Android 浏览器上却不行。 问题是展示中提供的 Example-Controller 中的内容类型:

    file = new DefaultStreamedContent(stream, "image/jpg", "some.pdf");
    

    仅适用于图像。好吧,大脑开启模式:

    file = new DefaultStreamedContent(stream, "application/pdf", "some.pdf"); 
    

    现在一切正常。

    【讨论】:

    • 嘿,感谢您的贡献!我已经在项目开发过程中尝试过您的方法:没有成功。此时我正在使用 PF Vers。 3.1。您能否提供您正在使用的 PF 版本?
    猜你喜欢
    • 2013-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-18
    • 2017-11-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多