Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java webapp: adding a content-disposition header to force browsers "save as" behavior

Even though it's not part of HTTP 1.1/RFC2616 webapps that wish to force a resource to be downloaded (rather than displayed) in a browser can use the Content-Disposition header like this:

Content-Disposition: attachment; filename=FILENAME

Even tough it's only defined in RFC2183 and not part of HTTP 1.1 it works in most web browsers as wanted.

So from the client side, everything is good enough.

However on the server-side, in my case, I've got a Java webapp and I don't know how I'm supposed to set that header, especially in the following case...

I'll have a file (say called "bigfile") hosted on an Amazon S3 instance (my S3 bucket shall be accessible using a partial address like: files.mycompany.com/) so users will be able to access this file at files.mycompany.com/bigfile.

Now is there a way to craft a servlet (or a .jsp) so that the Content-Disposition header is always added when the user wants to download that file?

What would the code look like and what are the gotchas, if any?

like image 724
SyntaxT3rr0r Avatar asked Mar 08 '10 23:03

SyntaxT3rr0r


2 Answers

I got this working as Pointy pointed out. Instead of linking directly to the asset - in my case pdfs - one now links to a JSP called download.jsp which takes and parses GET parameters and then serves out the pdf as a download.

Download here

Here's the jsp code I used. Its working in IE8, Chrome and Firefox:

<%@page session="false"
            contentType="text/html; charset=utf-8"
            import="java.io.IOException,
                    java.io.InputStream,
        java.io.OutputStream,
        javax.servlet.ServletContext,
        javax.servlet.http.HttpServlet,
        javax.servlet.http.HttpServletRequest,
        javax.servlet.http.HttpServletResponse,
        java.io.File,
        java.io.FileInputStream"
 %>
<%  
//Set the headers.
response.setContentType("application/x-download"); 
response.setHeader("Content-Disposition", "attachment; filename=downloaded.pdf");

[pull the file path from the request parameters]   

File file = new File("[pdf path pulled from the requests parameters]");
FileInputStream fileIn = new FileInputStream(file);
ServletOutputStream outstream = response.getOutputStream();

byte[] outputByte = new byte[40096];

while(fileIn.read(outputByte, 0, 40096) != -1)
{
    outstream.write(outputByte, 0, 40096);
}
fileIn.close();
outstream.flush();
outstream.close();

%>
like image 196
Jonathan McK Avatar answered Nov 20 '22 17:11

Jonathan McK


You wouldn't have a URL that was a direct reference to the file. Instead, you'd have a URL that leads to your servlet code (or to some sort of action code in your server-side framework). That, in turn, would have to access the file contents and shovel them out to the client, after setting up the header. (You'd also want to remember to deal with cache control headers, as appropriate.)

The HttpServletResponse class has APIs that'll let you set all the headers you want. You have to make sure that you set up the headers before you start dumping out the file contents, because the headers literally have to come first in the stream being sent out to the browser.

This is not that much different from a situation where you might have a servlet that would generate a download on-the-fly.

edit I'll leave that stuff above here for posterity's sake, but I'll note that there is (or might be) some way to hand over some HTTP headers to S3 when you store a file, such that Amazon will spit those back out when the file is served out. I'm not exactly sure how you'd do that, and I'm not sure that "Content-disposition" is a header that you can set up that way, but I'll keep looking.

like image 26
Pointy Avatar answered Nov 20 '22 18:11

Pointy