Link to download a resource as PDF, HTML or Excel

Hi,

I’m facing with this problem:

I have followed some advice on how to implement a Link button to download a resource:


public class SaveToExcelButton extends Link
{

    public SaveToExcelButton ()
    {
        super ();
        setCaption ( "Download Image" );
        setDescription ( "Download image to your local computer" );
        setTargetName ( "_blank" );
    }

      @Override
     public void attach() {
         super.attach(); // Must call.

         StreamResource.StreamSource source = new StreamResource.StreamSource() {

                                public InputStream getStream() {
                                   byte[] b = null;
                                   String ppp = "<td><span>djfdlkfj</span></td>";
                                    b=ppp.getBytes();
                                return new ByteArrayInputStream(b);
                                }
                        };
        StreamResource resource = new StreamResource ( source, "picture.pdf", getApplication() );
        resource.setMIMEType ( "application/pdf" );
        resource.setCacheTime ( 0 );

        setResource ( resource );
     }
}

but when I click on the link, aadin framework open a new window and show the file on the new browser window.

I need the popup where I can choice if I would save or not the file and not display it…
how can I do it?

You could change the mime type to “application/x-unknown”, that usually does the trick.

it seams to work but it’s not very clean…:slight_smile: :slight_smile:

Thnaks a lot

Never tried it myself, but
application/octet-stream
is more traditional content-type for “just treat it like binary data” – though I think a few browsers may still try to figure out the actual file type in their goofy attempt to be helpful.

Wonder if Vaadin can/does set the Content-Disposition which is often used for downloading files:
[font=Courier New]

Content-Dispostion: attachment;filename=“yourFileName.pdf”
[/font]

Perhaps you could set it with streamResource.getStream().setParameter(“Content-Disposition”, “foo”). Haven’t tried so can’t say for sure.

A less nice possibility would be to set it in the Response object that you get in a HttpServletRequestListener.

infact it doesn’t work… :frowning: :frowning: there is no method called “setParameter”


        StreamResource resource = new StreamResource ( source, "picture.xls", getApplication() );
        resource.getStreamSource().getStream().setParameter("Content-Disposition", "foo").

but what I think maybe vaadin developers should help us…download a resource is something “core” in a web framework.

Is it possible to do it with a Button?

You’re using getStream() from the StreamSource object, not StreamResource, as I said earlier. It returns an InputStream, not a DownloadStream, which has the method I mentioned.

Just say:

        resource.getStream().setParameter("Content-Disposition", "foo").

…and change the parameter value to the “attachment;filename=“yourFileName.pdf”” mentioned by David. -_-

Yes, you’ll have to use a ClickListener to handle the click and in the handler, use getWindow().open() to open the URL to the resource.

great works perfectly!!!

I’ll post all code for anyone needs it


package com.example.vaadin.view;

import com.vaadin.terminal.StreamResource;
import com.vaadin.ui.Link;
import java.io.ByteArrayInputStream;
import java.io.InputStream;

/**
 *
 * @author GiulianelliA
 */
public class SaveToExcelLink extends Link {

    public SaveToExcelLink() {
        super();
        setCaption("Download Image");
        setDescription("Download image to your local computer");
        setTargetName("_blank");
    }

    @Override
    public void attach() {
        super.attach(); // Must call.

        StreamResource.StreamSource source = new StreamResource.StreamSource() {

            public InputStream getStream() {
                byte[] b = null;
                String ppp = "<table><tr><td><span>djfdlkfj</span></td></tr></table>";
                b = ppp.getBytes();
                return new ByteArrayInputStream(b);
            }
        };


        String namefile = "picture.xls";
        StreamResource resource = new StreamResource(source, namefile, getApplication());

        resource.getStream().setParameter("Content-Disposition", "attachment;filename=\"" + namefile + "\"");
        resource.setMIMEType("application/xls");
        resource.setCacheTime(0);

        setResource(resource);
    }
}

I’ll try also with button

I’m finding several things I have to adjust/tweak/avoid/workaround in this area. Our users (of our older, pre-Vaadin UI) are used to clicking a link or button (depending on context) to download a dynamicaly generated Excel report file. Their browser (IE6+, FF, Chrome/Safari) prompts for opening/saving, or just saves, depending on the browser.

If we want to use a button, we have to either live with the IE content warning or open a new blank window that goes away once the download is completed. This second option is way too amateur for our application, and the first looks like a security problem, which is unacceptable for our customers.

We are stuck with links, which are less flexible. Even then, if we want to avoid opening a new window/tab just for the download, and don’t want to invalidate/replace the application window’s contents, I had to include a zero-sized “Browser” type Embedded component in my main layout,
with a debug id and external resource “javascript:false”
. I can then set PID_S{debug id} as the target of the link.

The embedded component is implemented as an IFRAME element, and named as indicated (that’s the UIDL ID used as the frame name).

This allows downloads without a new window or the IE warning, but only works with links. If you try to use window.open() in a button click listener, even with the hidden IFRAME name as the target, you get the IE warning, because the UIDL response containing the window.open call comes back asynchronously. IE can’t decide it was initiated by a direct user action (the button click), so it blocks it.

We will have to create a theme with a link style that looks like a button, and register all download link resources independently for a page. If we could use a button listener, then we only need to register one, and define the stream source dynamically on click based on the button context before opening the resource.

Unless someone has a slick way to make a synchronous response to a button click, that can execute server code before returning the response.

My goal is to avoid a URI handler, as that requires much more QA testing and input sanitizing (our app is a financial one, so security is everything).

Well, with the parameter “Content-Disposition” the target “_self” works well. The resource can be downloaded without a new window.
The only change to the solution above I had to do: Derive a new class from StreamResource and override getStream():

public class DownloadStreamResource extends StreamResource
{
	private static final long serialVersionUID = 1L;
	
	public DownloadStreamResource(StreamSource streamSource, String filename, Application application)
	{
		super(streamSource, filename, application);
		setCacheTime(0);
	}

	@Override
	public DownloadStream getStream()
	{
		DownloadStream stream = super.getStream();
		if(stream!=null)
		{
			stream.setParameter("Content-Disposition", "attachment;filename=\"" + getFilename()+ "\"");
		}
		return stream;
	}
}

Yes, setting Content-Disposition header does help. However, we are still seeing the Internet Explorer blocked site security bar if we try to use a server-side window.open(resource) call, as it is returned asynchronously.

We need to do this, because often our downloadable resources are reports, and the download is triggered by submitting a dialog window with a parameter form. We can’t find a way to both close the window and download the report without the security warning in IE. We need something that both allows a client-side direct link (anchor) style navigation AND sends an event to the server.

I’d rather not, but I think I need to write a custom client/server component based on the Link component :frowning:

Seems like there should be a reliable way to trigger a direct download without needing to write a URI handler or custom client-side component.

The
ActiveLink add-on
does just that - it is a Link that also sends events to listeners on the server.

I looked at ActiveLink, but it doesn’t “also” send events, it either sends events or performs a link navigation, not both. It only allows the default link behavior if there are NO server listeners OR the user remembers to click it with a modifier key. That’s not what we need.

On a single click event I want to:


I. commit a Form
    A) if it succeeds:
         1) close a popup modal Window
         2) trigger a browser download of a dynamically generated Resource based on the Form contents
    B) if the Form commit fails
         1) redisplay the Form with validation markers

I may be able to copy the ActiveLink code, though, and do both things at once, if I remove the modifier check code. That’s where I was going next. At least the download pathways cleanly do nothing if the resource DownloadStream is null.

The problem with a standard Button listener is that I.A.2 is asynchronous when it is done via a server-side listener call to Window.open(), and IE thinks it may be a security issue and blocks the download.

I don’t think it’s just IE that has this. Any modern browser with a popup blocker appears to use a simple check – if the user clicks on something so that the HTTP RESPONSE to that request is a browser popup, the popup generally opens okay. But if the popup opens any other way, it blocks it as a potential error, like popup ads and rubbish like that. I’ve seen similar in Safari/Chrome where you basically don’t see the window at all, but there’s an indicator in the upper right corner that you have to tell “allow popups from this site” to make it work.

For my purposes it is just IE - 6, 7, 8, (we don’t support 9 yet, but I haven’t heard about any changes in this area).

Vaadin actually has a limit that we did not have with our older version, which was page-at-a-time based. I can’t find any way to, in response to a
single user click
, both submit (and validate) user input in a Form and in response have the browser download a file (Content-Disposition=attachment) without opening a new browser window in IE just for the purpose of prompting the user to save or open the file.

Making the users click on two or more things to do what our old app could do with one click is not an option for us, but I can’t find any way around the IE download blocking, as all Vaadin communication is asynchronous, and IE doesn’t like that. I don’t want to use a target of “_top” or “_self”, as sometimes IE decides to ignore the Content-Disposition header and replace the page contents with the download, or at least make the current page stop responding. And it changes the URL in the address bar to a resource URL that is most likely a one-time address.

Hello Greg,

have you been able to solve the problem ?

We too have the same problem.

  1. The user click on a button to start generating the export
  2. We open a dialog with a progressbar
  3. In background in a separate thread the file is generated
  4. Once finished the dialog is closed and the file is opened via getMainWindow().open(DownloadResource)

Works fine for all browsers, with the exception of IE7+8 where the download warning is shown, and then (when confirmed to download) the message apears that the file could not be found

André

Sorry for the long thread necro, but this approach is impossible for vaadin 6.
StreamResource.getStream() constructs new DownloadStream every time it is called, so saving parameters to that is useless.

In Vaadin 7.0 and newer, you can attach a special FileDownloader extension to any component (preferably a Button); that enables the Button to behave as a Link in a way that upon clicking it will start the download process. If this is not possible (e.g. you’re trying to download from a MenuItem) then please see
https://vaadin.com/forum/#!/thread/1966628/1967475

Or you could use the
PopUp MenuBar
add-on :slight_smile: