0

I've been working on this for hours :

When a button is pressed I need to generate XML and download it to the user. I've tried using primefaces StreamedContent with p:fileDownload - but the only output I ever get is the .xhtml source of the actual page. I can use log statements to display the contents of the the StreamedContent before returning it and I can see my XML code correctly there, but the downloaded file always has the page source instead. (I've tried lots of other ways as well, tied downloading a jpg file, changing the bean to generate a response directly and calling the method through the action in the h:commandButton - always get the page source!).

Here's my .xhtml :

        <p:commandButton value="Create XML Bid/Offer" 
                 id="createXMLButton" 
             disabled="#{portfolioBean.noPortfolioSelected}"
                 ajax="false"
                 icon="ui-icon-arrowthichk-s">
    <p:fileDownload value="#{portfolioBean.order}" />  
</p:commandButton>

(alternate)

    <h:commandButton value="Create XML Bid/Offer" 
    id="createXMLButton" 
    disabled="#{portfolioBean.noPortfolioSelected}"
     ajax="false"
    icon="ui-icon-arrowthichk-s"
    action="#{portfolioBean.download()}" /> 

And the backing bean methods :

        public StreamedContent getOrder() {
...
}

(alternate)

        public String download() {
...
} 

I haven't included the method bodies because I know they return proper XML... it really seems to be something in the JSF magic. Is this a configuration issue or something?

Thanks!

UPDATE OK - I've stripped everything out of my code so it's just this :

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:p="http://primefaces.org/ui">
<h:head>
    <title>Edison Energy Portal</title>
</h:head>
<h:body>
    <h:form id="form">
        <h:commandButton value="Download PDF" action="#{downloadJPG.downloadFile}" />
     </h:form>
</h:body>
</html>

public class DownloadJPG {

// Constants ----------------------------------------------------------------------------------

private static final int DEFAULT_BUFFER_SIZE = 10240; // 10KB.

// Actions ------------------------------------------------------------------------------------

public void downloadFile() throws IOException {

    // Prepare.
    FacesContext facesContext = FacesContext.getCurrentInstance();
    ExternalContext externalContext = facesContext.getExternalContext();
    HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();

    File file = new File("....", "....");
    BufferedInputStream input = null;
    BufferedOutputStream output = null;

    try {
        // Open file.
        input = new BufferedInputStream(new FileInputStream(file), DEFAULT_BUFFER_SIZE);

        // Init servlet response.
        response.reset();
        response.setHeader("Content-Type", "application/pdf");
        response.setHeader("Content-Length", String.valueOf(file.length()));
        response.setHeader("Content-Disposition", "inline; filename=\"...\"");
        output = new BufferedOutputStream(response.getOutputStream(), DEFAULT_BUFFER_SIZE);

        // Write file contents to response.
        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
        int length;
        while ((length = input.read(buffer)) > 0) {
            output.write(buffer, 0, length);
        }

        // Finalize task.
        output.flush();
    } finally {
        // Gently close streams.
        close(output);
        close(input);
    }

    // Inform JSF that it doesn't need to handle response.
    // This is very important, otherwise you will get the following exception in the logs:
    // java.lang.IllegalStateException: Cannot forward after response has been committed.
    facesContext.responseComplete();
}

// Helpers (can be refactored to public utility class) ----------------------------------------

private static void close(Closeable resource) {
    if (resource != null) {
        try {
            resource.close();
        } catch (IOException e) {
            // Do your thing with the exception. Print it, log it or mail it. It may be useful to 
            // know that this will generally only be thrown when the client aborted the download.
            e.printStackTrace();
        }
    }
}

}

I've checked my source image file and it's not corrupt. BalusC's code worked for a single try, then I substituted and XML file for the current jpg, changed the MIME type and nothing has worked since. It's as if something bad has tripped in tomcat and I can't get it out or reset it (I've tried cleaning tomcat through eclipse, manually, even rebooted my box).

this is a real poser.

UPDATE : I've started eclipse clean - no luck. I'm really out of ideas. I have to think this is a configuration somewhere, because I know this code can work (I've seen it).

rwaltz
  • 3
  • 1
  • 3
  • 1
    How to download a document from JSF http://balusc.blogspot.com.es/2006/05/pdf-handling.html – SJuan76 Sep 20 '12 at 14:19
  • That worked - but it opens it in another window instead of launching the "view / save as" dialog. Thanks a bunch. I'm trying to adapt it to my XML, then I need it to download instead of display... any suggestions welcome. – rwaltz Sep 20 '12 at 20:01

1 Answers1

1

Given that nobody else (not even the mighty BalusC) has answered, I am posting it as a comment.

As how to download, again the BalusC blog. Worth browsing a little if you have spare time.

About the issue with how the browser manages it, it depends of browser handling, anyway most should honor the Content-Disposition header. Change this line in the example

response.setHeader("Content-Disposition", "inline; filename=\"" + getFileName() + "\"");

to

response.setHeader("Content-Disposition", "attachment; filename\"" + getFileName() + "\"");

reference

SJuan76
  • 24,532
  • 6
  • 47
  • 87
  • Thanks, I'd figured that out. I tried to map my xml output buffer to an input buffer and read from that instead of the file input, but it generated errors, so I just got one of the output files my XML task can generate and substituted it for the pdf file (changing the mime type) - and that immediately got the error again, the downloaded content was the page source. Now here's the kicker, when I put the code back to use the original file, the downloaded content is STILL the source. I'm using eclipse to test and I've tried cleaning tomcat and it's directories, but something has gone wrong. – rwaltz Sep 20 '12 at 21:26
  • Just reworked this to create a temp xml file and then try to stream the file (instead of trying to redirect an xml stream - I know, silly, but I'm grasping at this point). I can see both files, the created and the streamed. The created file is correct xml, but the streamed file contains the page source... same old problem. – rwaltz Sep 29 '12 at 01:44