Show-n-Tell Thursday

Forcing a file name when downloading a dynamically generated csv file

Over the last couple of weeks I've been working on a customisation project using IQJam as a starting point and then making it better fit a particular customer's requirements. It's worth mentioning as I finally had the justification to spend a little time investigating a problem which has been bugging me.

One of the features I added to IQJam was the ability to export data to Excel, a common enough feature that you've been able to do in Domino for ever. Simply print data out to the browser in simple HTML format and change the content-type of the page to "application/vnd.ms-excel". That's not the point of this posting really.

The problem I've been trying to work around is that if your user is using Excel 2007 or later (I'm only on the beta of Office 2010 but it still seems to be a problem for me anyway) and you use the printing HTML technique, Excel raises an error for the user when they load the page, something like:

The file you are trying to open, 'name.ext', is in a different format than specified by the file extension. Verify that the file is not corrupted and is from a trusted source before opening the file. Do you want to open the file now?

According to various technotes and blog posts there is no work around for this, it is a deliberate security feature of Excel. Fair enough I can live with this, though it's not acceptable to the end user, so instead of generating an Excel file I reverted to generated CSV formatted data.

The thing I learned from my investigations into the Excel problem was that there is this header which you can add to the data being generated which tells the browser that the page being opened is actually a file attachment and what the name of the file you want to download should be. So in my example, I'm able to create a unique file name every time the page is generated and also specify that it should be saved as a CSV file, not .XSP which is what it would be thanks to the page being loaded being an XPage. See this example:

try{
    var exCon = facesContext.getExternalContext();
    var writer = facesContext.getResponseWriter();
    var response = exCon.getResponse();
    response.setContentType("text/csv");
    response.setHeader("Content-disposition", "attachment; filename=export_" + DateConverter.dateToString(@Now(), "yyyyMMddhhmm") + ".csv");
    response.setHeader("Cache-Control", "no-cache");
    writer.write(getCSVBody());
    writer.endDocument();
    facesContext.responseComplete();
    writer.close();
}catch(e){
    _dump(e);
}

So this server side javascript sits in the afterRenderResponse of my XPage and is using the "web agents XPages style" technique which Stephan first documented to generate non HTML content from an XPage. The key line for this blog post is where the "Content-disposition" header is set, hopefully you can see where the filename is being created (I'm also using Tommy Valand's DateConverter SSJS code to get the current date / time formatted into a nice string).

Anyway, not a new technique looking at the dates on some of the internet postings out there, but a new one on me and worth passing on I thought.