Vaadin 10 Let user download a file

Hello,

I’m using Vaadin 10 Beta 1 and I can’t figure out how to let user download files from my application. I get access to java.io.InputStream of requested file through a REST request to a filehosting server. The filehosting server expects each file request to have a set of headers generated based on user profile data. Filehosting server cannot be reached by a user directly.

Options I considered:

  • simple <a href="url_to_my_file"></a> is unfortunately out of question due to header requirement and filehosting server being hidden to users.
  • Old FileDownloader from Vaadin 8 is not available in Vaddin 10 Beta 1.
  • Roundabout way of using <a></a> element where my Vaadin 10 client server has it’s own REST service which upon request fetches file from filehosting server and returns it. In this solution I need to keep a global guid-headers map used to retrieve headers required to fetch the file stream, this map would have to be updated everytime a user logs in or logs out. The html tag would look something like this <a href="local_rest_service_url?key=guid"></a>. I’d like to avoid this solution due to complexity involved.
  • Writing a javascript function to which I pass base64 encoded file data which triggers browser file download functionality. I’d like to avoid this solution due to requirement of fully storing the requested file in server memory before initiating download.

Is there a ‘proper’ way to allow user to download files in Vaadin 10?

If not, how can I pass java.io.InputStream contents to JavaScript function without first storing it all in Vaadin 10 app server memory?

You can allow users to download dynamic resources with StreamResources and Anchor:
Anchor downloadLink = new Anchor(getStreamResource(), "Download"); downloadLink.getElement().setAttribute("download", true);

-Olli

I think the most straightforward way is to use StreamResource. Quoting https://vaadin.com/docs/v10/flow/advanced/tutorial-dynamic-content.html:

...
NativeButton button = new NativeButton("Generate Image");
button.addClickListener(event -> {
    StreamResource resource = new StreamResource("image.svg",
            () -> getImageInputStream(name));
    image.setAttribute("data", resource);
});
...

private InputStream getImageInputStream(Input name) {
    String value = name.getValue();
    if (value == null) {
        value = "";
    }
    String svg = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>"
        + "<svg  xmlns='http://www.w3.org/2000/svg' "
        + "xmlns:xlink='http://www.w3.org/1999/xlink'>"
        + "<rect x='10' y='10' height='100' width='100' "
        + "style=' fill: #90C3D4'/><text x='30' y='30' fill='red'>"
        + value + "</text>" + "</svg>";
    return new ByteArrayInputStream(svg.getBytes(StandardCharsets.UTF_8));
}

Here getImageInputStream generates an InputStream with content.

Hello Artem, can you explain a little better how to download a file with the button? I’m not really sure how to implement this.

Hello Artem.

In the server I receive the File from a REST service. ( I can save in a folder in Server Hd , but I do not want do it )
But What I want is that the browser open the file with a "click "in a button.

I have the image from the REST server and I can create the StreamResource resource.

But I do not know how to return this to the browser.


Button downloadFile = new Button();
downloadFile.addClickListener(event → {
StreamResource resource = new StreamResource(docWraper.getItem().getRelativerPfad() ,
() → dokumentView.downloadDocument());
/// now what to do here ?
});

With an Anchor component all you need is set it up without caption/text and add a button component. Example:

  Anchor download = new Anchor(new StreamResource("filename.ext", () -> createResource()), "");
  download.getElement().setAttribute("download", true);
  download.add(new Button(new Icon(VaadinIcon.DOWNLOAD_ALT)));

Pay attetion that StreamResource is the com.vaadin.flow.server.StreamResource class.

Hi Al,

Is this only applicable for Tabular report format? Im having problem migrating my reports from V8 to V12 using JasperReport, so Im looking for other solutions.

Thanks!!

This is my code from V8

		Connection conn = DBConnection.connect();
        
        HashMap hm = new HashMap();
        hm.put("PROMO_ID", promoId);
        
        InputStream template = this.getClass().getResourceAsStream("/reports/PromoDealAcknowledgementFormReport.jasper");
        try {
            JasperPrint print = JasperFillManager.fillReport(template, hm, conn);
            file = File.createTempFile("output", ".pdf");
            JasperExportManager.exportReportToPdfFile(print, file.getPath());
        } catch (JRException | IOException ex) {
            Logger.getLogger(PromoDealAcknowledgementReport.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        StreamResource.StreamSource source = () -> {
            try {
                FileInputStream fis = new FileInputStream(file);
                return fis;
            } catch (Exception e) {
                e.getMessage();
                return null;
            }
        };
           

        StreamResource resource = new StreamResource(source, "PromoDealAcknowledgementForm.pdf");
        resource.setMIMEType("application/pdf");  

        VerticalLayout v = new VerticalLayout();
        v.setSizeFull();
        Embedded e = new Embedded();
        e.setSource(resource);
        e.setSizeFull();
        e.setType(Embedded.TYPE_BROWSER);
        v.addComponent(e);

Why Vaadin 14 does not have resource.setMIMEType, resource.getMIMEType?

Michal Lichvar:
Why Vaadin 14 does not have resource.setMIMEType, resource.getMIMEType?

I wonder if the [StreamResource::setContentType]
(https://vaadin.com/api/platform/14.1.20/com/vaadin/flow/server/StreamResource.html#setContentType-java.lang.String-) is what you are looking for.

I posted a Question and an Answer with example code on Stack Overflow on this issue.

Hello,

Do you guys know how to do this in using Designer? I just want to download a file whom path is stored on a Bean…
Using HTML anchor element maybe? But how can I set it up?