3

I have the following code, deployed on a https Asp site, build with MVC 4.0:

public FileResult ANotSoWorkingFunction(string filePath, string fileName)
{
 pathToFile = string.Format("~/{0}/{1}", pathToFile, fileName);
 return File(new FileStream(pathToFile, FileMode.Open), "application/pdf", fileName);
}

This will work (as you many of you probably already guessed) with Chrome, Firefox and IE9. But it will throw a:

---------------------------
Windows Internet Explorer
---------------------------
Internet Explorer cannot download someFileName from a_site.com.


Internet Explorer was not able to open this Internet site.  The requested site is either unavailable or cannot be found.  Please try again later.
---------------------------
OK   
---------------------------

On IE6,7,8

Any ideas or clues on this one are greatly appreciated as I already spend the hole day playing with html header.

EDIT:

Here are the header from IE7:

HTTP/1.1 200 OK
Cache-Control: private, no-cache="Set-Cookie"
Content-Type: application/pdf
Server: Microsoft-IIS/7.5
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
Set-Cookie: .ASPXAUTH=; expires=Mon, 11-Oct-1999 21:00:00 GMT; path=/; HttpOnly
X-Powered-By: ASP.NET
Date: Wed, 04 Apr 2012 08:43:50 GMT
Content-Length: 233324

And here are the ones from IE9:

HTTP/1.1 200 OK
Cache-Control: private, no-cache="Set-Cookie"
Content-Type: application/pdf
Server: Microsoft-IIS/7.5
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
Set-Cookie: .ASPXAUTH=; expires=Mon, 11-Oct-1999 21:00:00 GMT; path=/; HttpOnly
X-Powered-By: ASP.NET
Date: Wed, 04 Apr 2012 08:42:14 GMT
Content-Length: 233324

Thank you,

Calin
  • 6,042
  • 5
  • 42
  • 74
  • Can you show us your "Cache-Control" and "Set-Cookie" response headers? – Levi Apr 03 '12 at 21:27
  • Sure can Lezi, thank you for your intress in this – Calin Apr 04 '12 at 08:49
  • maybe is the same problem as in mvc 3 http://stackoverflow.com/questions/6943443/asp-mvc3-fileresult-with-accents-ie8-bugged –  Apr 19 '12 at 15:01

3 Answers3

8

I think I also ran into your problem.

I am also running IIS 7.5 and downloading a PDF through an action on an HTTPS request. For reasons I have yet to isolate, IIS 7.5 seems to be appending no-cache="Set-Cookie" to my Cache-Control response header regardless of what I set the Cache settings to on the Response. This was causing the fairly well documented no-cache issue on IE6, IE7, and IE8.

To resolve this, I made a small wrapper around the FileContentResult that cleared the headers, called the parent, then set the Cacheability to 'Private'. This side-stepped IIS 7.5's insistence to add no-cache="Set-Cookie" to the header, and the file downloaded properly in all browsers I tested. If you want to emulate what I did, first, here's my FileContentResult wrapper.

public class PdfContentResult : FileContentResult {

    public PdfContentResult(byte[] data) : base(data, "application/pdf") { }

    public PdfContentResult(byte[] data, string fileName) : this(data) {
        if (fileName == null) {
            throw new ArgumentNullException("fileName");
        }

        this.FileDownloadName = fileName;
    }

    public override void ExecuteResult(ControllerContext context) {
        context.HttpContext.Response.ClearHeaders();

        base.ExecuteResult(context);

        context.HttpContext.Response.Cache.SetCacheability(HttpCacheability.Private);
    }
}

Then I added an extension method to my ControllerExtensions so that it would be simple to find:

public static class ControllerExtensions {

    public static PdfContentResult Pdf(this Controller controller, byte[] fileContents, string fileName) {
        return new PdfContentResult(fileContents, fileName);
    }

}

Finally, within the Action, I did the equivalent of this:

public ActionResult MyGeneratedPdf() {
    byte[] myPdfContentInByteStream = GetPdfFromModel();
    return this.Pdf(myPdfContentInByteStream, "MyFile.pdf");
}

Obviously, if you're downloading all kinds of data types, you might not want to bind the workaround so closely to PDF.

Technetium
  • 4,717
  • 2
  • 34
  • 50
  • sounds promising will give it a try and let you know how it goes – Calin Jul 05 '12 at 21:23
  • 1
    One thing I would suggest you don't do is clear out the headers from the response! The reason for this is as follows. Forms authentication, windows identity foundation, etc. gives you a cookie which you pass around to identify yourself. If you support the concept of having a sliding user session then you issue a new cookie/token and this is added into the response headers via "Set-Cookie". Clearing the headers when downloading a file means this new cookie could be lost and your user is no longer authenticated! Not a good thing to happen. – Michael Ciba Jun 12 '14 at 10:26
1

We resolved this by changing the cache-control header before streaming the file.

Simplified code sample:

var browserInformation = Request.Browser;

//Set as private if current browser type is IE
Response.AppendHeader("cache-control", 
                    browserInformation.Browser == "IE" ? "private" : "no-cache");

return File(fileName, contentType, downloadFileName);

This worked (yay).. BUT I was left with a lack of clarity on why we had to do it this way for that specific site. We have four websites running on the same box, all under SSL, and only one had this header problem. I compared the web.config files and looked at the setup in IIS but couldn't shed any further light on why that one site needs those headers set explicitly.

If anyone has more to add on the above (for added clairty) that would be great.

solveig
  • 83
  • 7
0

In older versions of IE if a user tries to download a file over a HTTPS connection, any response headers that prevent caching will cause the file download process to fail. Below are most common headers which are causing the issue:

  • Cache-Control with the values no-cache or no-store
  • Vary with any value
  • Pragma with value no-cache

You can create an ActionFilterAttribute which will clear cache headers for you like this:

public class ClearCacheHeadersAttribute : FilterAttribute, IActionFilter
{
    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        return;
    }

    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        HttpContext.Current.Response.Headers.Remove("Cache-Control");
        HttpContext.Current.Response.Headers.Remove("Vary");
        HttpContext.Current.Response.Headers.Remove("Pragma");

        //Set the cache headers any way you like keeping in mind which values can brake the download
    }
}

And decorate yoour action with it:

[ClearCacheHeaders]
public FileResult ANotSoWorkingFunction(string filePath, string fileName)
{
    pathToFile = string.Format("~/{0}/{1}", pathToFile, fileName);
    return File(new FileStream(pathToFile, FileMode.Open), "application/pdf", fileName);
}
tpeczek
  • 22,947
  • 3
  • 69
  • 75
  • Thank you for you answer, I did tried this in my searches yesterday but with no luck – Calin Apr 04 '12 at 08:46
  • For some reason it will still add the Cache-Control header – Calin Apr 04 '12 at 08:54
  • Please check configuration/system.webServer/staticContent node in your Web.config or in IIS config file. You may also try setting Cache-Control to some allowed value yourself, for example: HttpContext.Current.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches); HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.Public); – tpeczek Apr 04 '12 at 09:45
  • No luck, checked web.config %windir%\Microsoft.NET\Framework\framework_version\CONFIG; %windir%\Microsoft.NET\Framework\framework_version\CONFIG; %windir%\system32\inetsrv\config – Calin Apr 04 '12 at 10:30
  • tried to update to HttpCacheability public the header updates but the downloa still does not work – Calin Apr 04 '12 at 10:55
  • This will throw exceptions in many production server environments as `Response.Headers.Remove` (and `Response.Headers.Add`) are disabled in non-integrated mode. – Keith Oct 29 '12 at 17:27