0

I use JSF and I have an h:commandButton to prompt for a file download. The file is in a PDF format. The downloaded file has correct number of pages but they are blank.

When the file is opened in the browser I get this message:

This PDF document might not be displayed correctly.

This is my commandButton:

    <h:form>
        <h:commandButton action="#{fileDownloadView.fileDownloadView}" value="Download"/>
    </h:form>

And this is my class:

@ManagedBean
public class FileDownloadView {

    private static final String FILENAME = "manual.pdf";
    private static final String CONTENT_TYPE = "application/pdf";

    public FileDownloadView() throws IOException, DocumentException {
        Resource resource = new ClassPathResource(FILENAME);
        InputStream stream = resource.getInputStream();
        FacesContext facesContext = FacesContext.getCurrentInstance();
        ExternalContext externalContext = facesContext.getExternalContext();
        externalContext.responseReset();
        externalContext.setResponseContentType(CONTENT_TYPE);
        externalContext.setResponseHeader("Content-Disposition", "attachment; filename=\"" + FILENAME + "\"");
        OutputStream outputStream = externalContext.getResponseOutputStream();
        byte[] bytes = IOUtils.toByteArray(stream);
        outputStream.write(bytes);
        facesContext.responseComplete();
    }

}

What could be the cause of this?

EDIT:

Claimed duplicate post gives this pice of code:

public void download() throws IOException {
    FacesContext fc = FacesContext.getCurrentInstance();
    ExternalContext ec = fc.getExternalContext();

    ec.responseReset(); // Some JSF component library or some Filter might have set some headers in the buffer beforehand. We want to get rid of them, else it may collide.
    ec.setResponseContentType(contentType); // Check http://www.iana.org/assignments/media-types for all types. Use if necessary ExternalContext#getMimeType() for auto-detection based on filename.
    ec.setResponseContentLength(contentLength); // Set it with the file size. This header is optional. It will work if it's omitted, but the download progress will be unknown.
    ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // The Save As popup magic is done here. You can give it any file name you want, this only won't work in MSIE, it will use current request URL as file name instead.

    OutputStream output = ec.getResponseOutputStream();
    // Now you can write the InputStream of the file to the above OutputStream the usual way.
    // ...

    fc.responseComplete(); // Important! Otherwise JSF will attempt to render the response which obviously will fail since it's already written with a file and closed.
}

If you look closer my code is the same with the exception of the comment part that writes:

// Now you can write the InputStream of the file to the above OutputStream the usual way. // ...

What i have writen for this is

byte[] bytes = IOUtils.toByteArray(stream);
outputStream.write(bytes);

What is wrong with that? Isn't that a byte array that is written in the output stream? Why is this not working?

  • So if you use an `h:commandButton` the pages are not empty? Or if you return a textfile it is not empty? – Kukeltje Jan 28 '19 at 10:29
  • The pages are empty either way (p:commandButton or h:commandButton) – Paris Karagiannopoulos Jan 28 '19 at 10:43
  • So the code changes I made above are not working either? So you checked if in this whole process, if you, for testing, use a FileOutputStream instead of from the external context, you get a file with the right content? – Kukeltje Jan 28 '19 at 11:07
  • Hmm I never saw a bean constructor used as action method - is that intended to work? – Selaron Jan 28 '19 at 11:22
  • @Selaron: The (now removed comment) that they do it in PF is 'correct', with the difference that it is used with [the `p:fileDownload`](https://www.primefaces.org/showcase/ui/file/download.xhtml) and in a completely different way. – Kukeltje Jan 28 '19 at 11:31
  • @Kukeltje but what does `#{fileDownloadView.fileDownloadView}` resolve to when assuming the provided bean source code is complete? – Selaron Jan 28 '19 at 11:37
  • are there any alternatives? I tried click but it didn't find the file and I got a 404 – Paris Karagiannopoulos Jan 28 '19 at 11:59
  • 1
    Possible duplicate of [How to provide a file download from a JSF backing bean?](https://stackoverflow.com/questions/9391838/how-to-provide-a-file-download-from-a-jsf-backing-bean) – Kukeltje Jan 28 '19 at 12:13
  • It does not, that is why I put 'correct' in quotes. The do do work in the constructor, but still have a getter (which I overlooked ;-)) – Kukeltje Jan 28 '19 at 12:14
  • The link you provided is what I based my code upon. It does not work – Paris Karagiannopoulos Jan 28 '19 at 12:14
  • It does work for hundreds op people. Did you look at the comments by @Selaron? – Kukeltje Jan 28 '19 at 12:18
  • I don't this that I made the problem clear. I think the problem is with the Java code and the way the output stream is written. Primefaces do download the file, it is just that the downloaded file is blank. – Paris Karagiannopoulos Jan 28 '19 at 12:21
  • 1
    You **DO** have a wrong piece of code, both in the java and xhtml and @Seleron commented on that : _"Hmm I never saw a bean constructor used as action method - is that intended to work?"_ and for your possible error in java, I wrote another comment: _"if you, for testing, use a FileOutputStream instead of from the external context, you get a file with the right content?"_ And I mentioned something about testing a non-pdf file too... Please be a little more pro-active. – Kukeltje Jan 28 '19 at 12:23
  • This is what I was here for. I needed a sample code in Java – Paris Karagiannopoulos Jan 28 '19 at 12:26
  • **There is samplecode in java in the duplicate**... Fully working. Your code is **not** like in the duplicate. @Selaron: Can you write an answer so OP actually sees what you mean in code and explicitly point out the errors that are made here... – Kukeltje Jan 28 '19 at 12:27
  • I edited my original post so that you can see that my code is the same – Paris Karagiannopoulos Jan 28 '19 at 12:32
  • I've tested your code and it works for me. But if you use `"Content-Disposition", "attachment;...` the browser should ask you, if you want to save your file. Save the file and compare the content with the original one. Which kind of browser do you use? - By the way, your Method `FileDownloadView` looks a little bit strange - no return type and it starts with a capital letter. I think this is only a _typo_ – drkunibar Jan 28 '19 at 16:38
  • Your code is **NOT** the same, just like 3 people already posted. `public FileDownloadView() throws IOException, DocumentException {` is NOT the same as `public void download() throws IOException {` And if @drkunibar is right and it is a typo (which it might be) then you for a part make us chase ghosts. And you still did not respond on the download of plain text and the saving to a file! The only thing you keep saying is that your code is the same. And maybe your original PDF is wrong, or your PDF reader is corrupt or... – Kukeltje Jan 28 '19 at 16:45
  • So seriously, we have been chasing a ghost: https://stackoverflow.com/questions/54439864/downloading-resources-using-jsf... Please, please, please be more investigative before posting problems/questions ( – Kukeltje Jan 30 '19 at 13:20
  • All I know is that I want a presumably very simple question answered and I have to go through stackOverflow bureaucracy to find solution to my problem. – Paris Karagiannopoulos Jan 30 '19 at 13:24

1 Answers1

2

You are right that the body of your beans' constructor does the same as the body of the download method given in the example.

Your command link <h:commandButton action="#{fileDownloadView.fileDownloadView}" .../> action method expression is trying to find and invoke a method named fileDownloadView on your bean but that method does not exist.

Your public FileDownloadView is a constructor because it has no return value type and the same name as the class. If you'd change that to public void download the bean would have no explicit constructor anymore but finally a method that is invokable by a command button like this:

 <h:commandButton action="#{fileDownloadView.download}" value="Download"/>

Capitalization matters, so don't call the method public void Download or the like.

I'm not sure what actually happens in your current implementation, but I guess while #{fileDownloadView.fileDownloadView} is beeing resolved, there is a new instance created of the bean fileDownloadView from first part of the expression and the code in constructor is successfully executed. Some CPU cylcles later the ELResolver fails to resolve second .fileDownloadView part of the expression and throws an exception which kind of messes things up.

Selaron
  • 6,105
  • 4
  • 31
  • 39