7

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?

SyntaxT3rr0r
  • 27,745
  • 21
  • 87
  • 120
  • so, if file will be downloaded from s3, and http request will be made to the s3 server, then s3 must be adding this header, not your application, isn't it? – Igor Artamonov Mar 08 '10 at 23:28
  • @splix: seen your comment, I guess so... But figuring that out is kinda precisely the whole point of my question :) If you make an answer detaliling this a tiny bit more I'll upvote and accept :) – SyntaxT3rr0r Mar 08 '10 at 23:34
  • @splix: seen your comment, I googled on S3 and found that apparently due to popular request you can *"Upload with custom headers"* (files to your bucket) which would be a great way to solve my issue! – SyntaxT3rr0r Mar 08 '10 at 23:41
  • 1
    In fact it looks like the Amazon management UI lets you set up headers directly, if that works for you. Thanks for asking this question!! – Pointy Mar 08 '10 at 23:42
  • I searched around, but I can't find any references that S3 supports JSP/Servlet. Why did you tag it? The only JSP/Servlet solution would be to play for a proxy on your own host and set the content disposition header programmatically, but that would make the main purpose of S3 (a CDN so that you can save connections/requests on your primary domain) completely pointless, you could then as good just host it yourself. – BalusC Mar 09 '10 at 00:10
  • @BalusC: the tag may be bogus but I didn't know that using S3 was the way to go. I'm my own stupidity I thought the only way to make it work was to somehow "wrap" the S3 file call from my Java Webapp and to set the headers from there, hence the mistag. But the question is still interesting, for example Pointy thanked me for asking the question: others may be trying to do the same (wrongly from Java/their webapp) while there's another way :) – SyntaxT3rr0r Mar 09 '10 at 00:33
  • Content-Disposition is part of HTTP; it's defined as an HTTP header field in the IANA header field registry (http://www.iana.org/assignments/message-headers/perm-headers.html) and the current definition is in RFC 6266. – Julian Reschke Oct 26 '11 at 07:04

4 Answers4

4

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();

%>
2

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.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • @Pointy: so I take it it's not compatible with the S3 case I explained in my question? I mean, if I have to stream the file through my server it's kinda defeating the whole S3 point isn't it!? – SyntaxT3rr0r Mar 08 '10 at 23:35
  • Yes I agree but see my comment - I confess to being a lightweight when it comes to S3 details, but I'm looking ... – Pointy Mar 08 '10 at 23:37
0

Put a .htaccess file in the root folder with the following line:

Header set Content-Disposition attachment
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
0

I just found this via google.

And I had a simmilar problem, but I still want to use a Servlet (as I generate the Content).


However the following line is all you need in a Servlet.

response.setHeader("Content-Disposition", "attachment; filename=downloadedData.json");