Export data to Excel

Hi,
I have created a Excel workbook with Apache POI and I’m able to write it on a file on the server.
This is my code:

HSSFWorkbook workbook = new HSSFWorkbook(); 
HSSFSheet firstSheet = workbook.createSheet("my sheet"); 
HSSFRow rowA = firstSheet.createRow(0);
HSSFCell cellA = rowA.createCell(0); 
cellA.setCellValue(new HSSFRichTextString("my sheet"));

FileOutputStream fos = null; 

fos = new FileOutputStream(new File("CreateExcelDemo.xls")); 
workbook.write(fos); 

fos.flush(); 
fos.close(); 

Instead of writing to a file, I want the user is prompted to open or download the file. I tried with StreamResource, but I couldn’t find the way, because it seems to be built for InputStream and not for OutputStream.
Can anyone help me?
Thank you, Francesco

Hi Franesco,

We do this in our application; I suspect there is an easier way to do it, but what we do is this.

  1. We write the excel file to a temporary file.
  2. We’ve create a DownloadResource that takes the data from a file, and a DeletingFileInputStream that deltes the file when it is closed.
  3. Cut-n-pasted code below

    File tempFile = File.createTempFile("tmp", ".xls");
    // Create contents here, using POI, and write to tempFile
    TemporaryFileDownloadResource resource = new TemporaryFileDownloadResource(getApplication(),
                "default-name-of-file.xls", "application/vnd.ms-excel", file);
            getWindow().open(resource, "_self");

Classes referred to :


public class TemporaryFileDownloadResource extends StreamResource {

  private final String filename;
  private String contentType;

  public TemporaryFileDownloadResource(Application application, String fileName, String contentType, File tempFile) throws FileNotFoundException {
    super(new FileStreamResource(tempFile), fileName, application);
    this.filename = fileName;
    this.contentType = contentType;
  }

  public DownloadStream getStream() {
    DownloadStream stream = new DownloadStream(getStreamSource().getStream(), contentType, filename);
    stream.setParameter("Content-Disposition", "attachment;filename=" + filename);
    // This magic incantation should prevent anyone from caching the data
    stream.setParameter("Cache-Control", "private,no-cache,no-store");    
    // In theory <=0 disables caching. In practice Chrome, Safari (and, apparently, IE) all ignore <=0. Set to 1s 
    stream.setCacheTime(1000);
    return stream;
  }

  private static class FileStreamResource implements
      StreamResource.StreamSource {

    private final InputStream inputStream;

    public FileStreamResource(File fileToDownload)
        throws FileNotFoundException {
      inputStream = new DeletingFileInputStream(fileToDownload);
    }

    public InputStream getStream() {
      return inputStream;
    }
  }

/**
 * This input stream deletes the given file when the InputStream is closed; intended to be used with temporary files.
 *
 */
public class DeletingFileInputStream extends FileInputStream {
  protected File file = null;
  public DeletingFileInputStream(File file) throws FileNotFoundException {
    super(file);
    this.file = file;
  }

  @Override
  public void close() throws IOException {
    super.close();
    file.delete();
  }
}



Try using the PipedInputStream/PipedOutputStream. It is a bit tricky but is an elegant solution :slight_smile:

You need to write your bytes to a pipe, and tell Vaadin to read from that pipe.
See
http://vaadin.com/forum/-/message_boards/message/124004#_19_message_124067
for code.

Charles,
Thank you for your code, it works very well.
Francesco

Thanks so much to Charles Anthony for that example code. I got it to work for downloading PDF documents stored outside the web server’s purview.

Question:
How can I make this open in a new browser window?

Despite your code setting “Content-Disposition”, on some browsers the file is opened in a browser window rather than being saved. For example, I am seeing this behavior in iPad > Safari and Eclipse > built-in browser (WebKit?). That show-on-screen behavior would be OK, except that when the user hits the browser’s Back button the Vaadin app restarts. Restarting Vaadin app is a bad thing. Opening the loaded file in a new separate browser window would solve that problem. But I’m not savvy enough about web technology to know how to force a separate window.

Some googling led me to tips about using an attribute of target=‘_blank’ on an HTML ‘href’ tag as shown in the following snippet. But I have no idea how to translate that to this Vaadin programmatically controlled download.

<a 
  href="http://www.pageresource.com/linkus.htm" 
  target="_blank"
>
Link to us!</a> 

–Basil Bourque

It should be enough, if you take Charles source code and replace the “_self” with “_blank”.

Andreas

Thank you for that information. I did not know about this feature of HTML. And I did not realize that the answer was outside the "
TemporaryFileDownloadResource
" class, and was actually in the line:

getWindow().open(resource, "_self");

What I learned…

In a normal web page (outside Vaadin), you can have a ’
target
’ attribute on an ’
a
’ tag, to ask the web browser to open the link in a certain way. Example:

<a href="http://www.example.com/" [color=#00eb17]
target="[color=#e00101]
_blank
[/color]"
[/color]>Visit Example Company!</a> 

Values for “target” include:


_blank

This asks the browser to open the link in a new browser window (or tab).


_self

This asks the browser to open the link in the same browser window.

You can read more in the
HTML5 specification
.

Vaadin is setting these target values on your behalf when you pass the desired value as the window name argument in the call to
Window.open()
.

Keep in mind that setting this
target
value is only a request we are making of the web browser. Web browsers (or other ‘user agents’) are free to do what ever they want. Such is life as a web app programmer.

So here is my source code for calling my modified version of download resource class. I modified it to not delete the files after downloading. In this example I am letting the user download a PDF file.

Button downloadButton = new Button( "Bogus download…" );
  downloadButton.addListener( new Button.ClickListener() {

      @Override
      public void buttonClick( ClickEvent event ) {
          String filePath = "/Users/basilbourque/Desktop/my_important_document.pdf";
          File file = new File( filePath );
          FileDownloadResource resource = null;
          try {
              resource = new[color=#00d824]
 FileDownloadResource( getApplication(), "My Important Document 2012.pdf", "application/pdf", file );
[/color]
          } catch ( FileNotFoundException e ) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
          getWindow().open( resource, [color=#d80000]
"_blank"
[/color] ); // "_self" = same browser window. "_blank" = new browser window/tab.
          System.out.println( "DEBUG - Finished downloading PDF file. " + new DateTime().toString() );
      }
  } );
  this.addComponent( downloadButton );

Hi,

I’m using the TemporaryFileDownloadResource and it’s working like a charm on http, but when i use the https i’m having issues.

i read in others posts not related to vaadin that it’s related to Cache-preventing headers.

Can anyone help?

Thanks In advance

I’m sorry I can’t help you Dany, just posting an error I’ve found when using the code:

stream.setParameter("Content-Disposition", "attachment;filename=" + filename);

should be

stream.setParameter("Content-Disposition", "attachment;filename=\"" + filename + "\"");

to prevent
this issue
in Firefox.

where can find source code .

张 美玲, my post was refered to the code posted by Charles Anthony

Here:

https://code.google.com/p/tableexport-for-vaadin/source/browse/src/com/vaadin/addon/tableexport/TemporaryFileDownloadResource.java?r=aeddbd4d688d731f7a5eebfe97f9e12fe8de83ca