Here's how I'd do this:
- Create an HttpHandler that generates the document, saves it (in cache/on disk/database, your call) and returns an identifier for the generated document
- Call the HttpHandler using an AJAX call (use jQuery to make this really easy)
- Use javascript/jQuery to display a waiting message/graphic/animation/whatever
- In the AJAX request callback, redirect to a page that takes the generated identifier and sends the corresponding document as an attachment using the Content-Disposition header.
As a side note, this would be a little more straightforward in ASP.NET MVC
GenerateDocument HttpHandler stub:
public class GenerateMyDocumentHandler : IHttpHandler
{
#region [ IHttpHandler Members ]
public Boolean IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
var docIdentifier = this.GenerateDocument();
context.Response.ContentType = "text/plain";
context.Response.Write(docIdentifier.ToString("N"));
}
#endregion
private Guid GenerateDocument()
{
var identifier = Guid.NewGuid();
// Logic that generates your document and saves it using the identifier
return identifier;
}
}
Client Script stub:
function generateDocument() {
var result = $.get('/GenerateDocument.ashx', { any: 'parameters', you: 'need', go: 'here' }, generateDocumentCallback, 'text');
// display your waiting graphic here
}
function generateDocumentCallback(result) {
window.location.href = '/RetrieveDocument.ashx/' + result;
}
RetrieveDocument HttpHandler stub:
public class RetrieveDocument : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
var identifier = new Guid(context.Request.Url.Segments[1]);
var fileContent = this.GetFileAsBytes(identifier);
context.Response.ContentType = "application/vnd.ms-excel";
context.Response.AddHeader("Content-Disposition", "attachment; filename=yourfilename.xls");
context.Response.OutputStream.Write(fileContent, 0, fileContent.Length);
}
private Byte[] GetFileAsBytes(Guid identifier)
{
Byte[] fileBytes;
// retrieve the specified file from disk/memory/database/etc
return fileBytes;
}
public Boolean IsReusable
{
get
{
return true;
}
}
}