1

I'm trying to send a view that contains a html table as a downloadable file to the user, as an excel file.

I keep getting the error "Server cannot set content type after HTTP headers have been sent.". I can't figure out what's going wrong...

Here's some code:

Excel.aspx:

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<html>
<head runat="server">
    <title>Excel</title>
</head>
<body>
....
</body>
</html>

ControllerAction:

   public FileResult Excel()
    {
        string view = RenderViewToString(this.ControllerContext, "~/Views/Shared/Excel.aspx", null, this.ViewData, this.TempData);

        MemoryStream stream = new MemoryStream(System.Text.Encoding.ASCII.GetBytes(view));

        string mimetype = RainbowsDotNet.FileHandling.MimeType.GetMimetypeFromExtension(".xls");

        FileStreamResult filestreamresult = new FileStreamResult(stream, mimetype);
        filestreamresult.FileDownloadName = "Employees_{0}.xls".FormatWith(DateTime.Now.ToString("ddMMyyyy_HHmmss"));

        return filestreamresult;
    }

While debugging, string "view" contains:

"\r\n<html>\r\n<body>............................"

Any idea? I do about the exact same thing with a blob and that nicely returns a document to download.

Thomas Stock
  • 10,927
  • 14
  • 62
  • 79
  • I know this is obvious, but your headers have already been sent to the client so you need to find out why. What is triggering that the headers have already been sent. Possibly you do not have buffering enabled? Are you purposely disabling buffering somewhere else? – Jeff Widmer Aug 06 '09 at 14:40
  • your "view" data is a YSOD (Yellow Screen Of Death), i'd start there – mxmissile Aug 06 '09 at 14:45
  • hmm, I didn't touch the buffering, but still. I do exactly the same as in another controller where I return also documents and it works fine there... just a FileResult that returns a FileStreamResult.. – Thomas Stock Aug 06 '09 at 14:47
  • what do you mean, mxmissile? My "view" variable contains the html with the table and the many rows.. it seems fine. – Thomas Stock Aug 06 '09 at 14:49

2 Answers2

4

You could get around this issue by putting this in the controller. It allows you to put HTML in a view, and then send it to the browser.

public ActionResult Excel()
{
    this.Response.AddHeader("Content-Disposition", "Employees_{0}.xls".FormatWith(DateTime.Now.ToString("ddMMyyyy_HHmmss")));
    this.Response.ContentType = "application/vnd.ms-excel";
    //Do model stuff
    Model model = new Model();
    return View(model);
}

Sounds hacky? It is a little. I had the same problem you mentioned, and the question Jeff points to is also one of mine. :)

As mentioned in my comment, you should ensure that your view does not have the following:

<html>
  <head>
  ...
  </head>
  <body>
  </body>
</html>

None of this is needed, and may result in your page being rendered as HTML, rather than the Excel document being returned. So all you'll have rendered is the actual table tags and everything inside.

Dan Atkinson
  • 11,391
  • 14
  • 81
  • 114
  • does this work for you? All it does here is show the HTML in the browser – Thomas Stock Aug 07 '09 at 07:22
  • Sorry, I'll delete my last comment. I meant it DOES work! :) But, you could try putting the this.Response code inside the view. Either way, it should work. Also, you may want to ensure that the view (or most likely, the master) does not contain anything outside the table tags (html, head, body). – Dan Atkinson Aug 07 '09 at 08:21
  • Unfortunately, this won't work for me, where I need to render 20 or 30 views to strings (email templates). Any other ideas, no matter how hacky? – James S Aug 07 '09 at 08:38
  • Why won't it work? You can easily create a master page that has no content except for the – Dan Atkinson Aug 07 '09 at 08:49
1

Did you use the RenderViewToString method from this post: Render a view as a string?

If you did then there is a Response.Flush in that code which is sending the headers. Buffering is enabled by default but if you call Response.Flush then everything is sent down to the client. And then why you try to send the file with the updated headers, you get that error.

Community
  • 1
  • 1
Jeff Widmer
  • 4,746
  • 6
  • 37
  • 51
  • Yeah that's the one I use! That seems to be the problem. Now, how do you solve it? :-) I'd like to keep using that method. – Thomas Stock Aug 06 '09 at 14:50
  • Not sure... It definitely does not feel correct that you need to call a Flush in RenderViewToString in order to get the string contents of a view. I wonder if there is some other way of writing the view to a stream directly? – Jeff Widmer Aug 06 '09 at 14:55
  • No idea.. I guess this is my punishment for using code I don't understand. – Thomas Stock Aug 06 '09 at 14:58
  • I spent a lot of time 6 months ago trying to take remove the Response.Flush() (I was using it to create email templates, then trying to redirect) but wasn't successful. I hope someone can solve it. – James S Aug 06 '09 at 15:03
  • I don't remember the exact solution but this post helped me in the right direction.. marking as answer – Thomas Stock Sep 14 '10 at 09:11