Question

In an ASP.NET application, I'm using iTextSharp (PdfStamper, mostly) to fill in some content on a PDF and send it to the user. The following code is inside an OnClick event:

PdfReader r = new PdfReader(
  new RandomAccessFileOrArray(Request.MapPath(compatiblePdf)), null
);

ps = new PdfStamper(r, Response.OutputStream);
AcroFields af = ps.AcroFields;

af.SetField("ContactInfo[0]", o.mallName);
af.SetField("ClientName", string.Format("{0} {1}", c.firstName, c.lastName));
af.SetField("ClientEmail", c.emailAddress);
ps.FormFlattening = true;
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "attachment; filename=Form.pdf");
ps.Close();
Response.End();

So, basically, PdfReader gets the file, PdfStamper takes the PdfReader as an argument, and will push its finished PDF to Response.OutputStream.

The problem is that, with IE and Adobe Reader, if you select "Open" from the file dialog, Adobe Reader throws an error saying "file could not be found". Users can "Save" the file just fine, and even starting the download again (clicking "Open" again when asked) seems to work. But on a new machine that's never downloaded the file, Adobe Reader seems to misplace the file between it going to the Temporary Files or whatever IE does.

I can only imagine one thing right now: Response.End(), should, maybe, be Response.Close() instead, or maybe the whole thing should have Response.Flush() before it. But I don't know for sure that that won't make the problem worse, and I'm having a hard time testing (because, once you've downloaded the file once, the error isn't thrown again).

Might that fix the problem? Do I have something wrong in the headers? Or is there something else I should be doing with the Response / PdfStamper objects?

Was it helpful?

Solution

Whenever I force content at a user, I follow this set of steps for the Response:

Response.Clear()
Response.ClearHeaders()
Response.Buffer = True
Response.ContentType = "your mime type"
Response.CacheControl = "public"
Response.AddHeader("Pragma", "public")
Response.AddHeader("Expires", "0")
Response.AddHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0")
Response.AddHeader("Content-Description", "Description of your content")
Response.AddHeader("Content-Disposition", "attachment; filename=""somefile.pdf""")

' Add your content to the buffer here

Response.Flush()
Response.End()

This seems to be pretty good at preventing all the "file not found" garbage.

EDIT: For those interested in what these headers actually mean:

  1. Pragma: public helps control cache for backwards compatibility with HTTP/1.0 requests. It ensures that your request makes it to the server even if there's already a cached response.
  2. Expires: 0 is an interval in seconds after which the response expires. Setting to 0 expires the response immediately, helping avoid a stale cache.
  3. Cache-control: must-revalidate tells the cache that it must obey your every command (i.e. it must give you a new response when you ask for it).
  4. Cache-control: post-check=0, pre-check=0: this is an interval in seconds that a response must be checked for freshness after/prior to (respectively) being served the content. Setting to 0 forces freshness of the response to be checked immediately. (More at MSDN.)
  5. The remainder are simply describing the content you want the user to receive. Specifying "attachment" tells the browser to offer the file as a download and to not display it inline.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top