Returning a file to View/Download in ASP.NET MVC

435,061

Solution 1

public ActionResult Download()
{
    var document = ...
    var cd = new System.Net.Mime.ContentDisposition
    {
        // for example foo.bak
        FileName = document.FileName, 

        // always prompt the user for downloading, set to true if you want 
        // the browser to try to show the file inline
        Inline = false, 
    };
    Response.AppendHeader("Content-Disposition", cd.ToString());
    return File(document.Data, document.ContentType);
}

NOTE: This example code above fails to properly account for international characters in the filename. See RFC6266 for the relevant standardization. I believe recent versions of ASP.Net MVC's File() method and the ContentDispositionHeaderValue class properly accounts for this. - Oskar 2016-02-25

Solution 2

I had trouble with the accepted answer due to no type hinting on the "document" variable: var document = ... So I'm posting what worked for me as an alternative in case anybody else is having trouble.

public ActionResult DownloadFile()
{
    string filename = "File.pdf";
    string filepath = AppDomain.CurrentDomain.BaseDirectory + "/Path/To/File/" + filename;
    byte[] filedata = System.IO.File.ReadAllBytes(filepath);
    string contentType = MimeMapping.GetMimeMapping(filepath);

    var cd = new System.Net.Mime.ContentDisposition
    {
        FileName = filename,
        Inline = true,
    };

    Response.AppendHeader("Content-Disposition", cd.ToString());

    return File(filedata, contentType);
}

Solution 3

Darin Dimitrov's answer is correct. Just an addition:

Response.AppendHeader("Content-Disposition", cd.ToString()); may cause the browser to fail rendering the file if your response already contains a "Content-Disposition" header. In that case, you may want to use:

Response.Headers.Add("Content-Disposition", cd.ToString());

Solution 4

I believe this answer is cleaner, (based on https://stackoverflow.com/a/3007668/550975)

    public ActionResult GetAttachment(long id)
    {
        FileAttachment attachment;
        using (var db = new TheContext())
        {
            attachment = db.FileAttachments.FirstOrDefault(x => x.Id == id);
        }

        return File(attachment.FileData, "application/force-download", Path.GetFileName(attachment.FileName));
    }

Solution 5

Below code worked for me for getting a pdf file from an API service and response it out to the browser - hope it helps;

public async Task<FileResult> PrintPdfStatements(string fileName)
    {
         var fileContent = await GetFileStreamAsync(fileName);
         var fileContentBytes = ((MemoryStream)fileContent).ToArray();
         return File(fileContentBytes, System.Net.Mime.MediaTypeNames.Application.Pdf);
    }
Share:
435,061
Nick Albrecht
Author by

Nick Albrecht

Enthusiast with a passion for software and technology innovations. Ohhh, shiney.

Updated on July 08, 2022

Comments

  • Nick Albrecht
    Nick Albrecht almost 2 years

    I'm encountering a problem sending files stored in a database back to the user in ASP.NET MVC. What I want is a view listing two links, one to view the file and let the mimetype sent to the browser determine how it should be handled, and the other to force a download.

    If I choose to view a file called SomeRandomFile.bak and the browser doesn't have an associated program to open files of this type, then I have no problem with it defaulting to the download behavior. However, if I choose to view a file called SomeRandomFile.pdf or SomeRandomFile.jpg I want the file to simply open. But I also want to keep a download link off to the side so that I can force a download prompt regardless of the file type. Does this make sense?

    I have tried FileStreamResult and it works for most files, its constructor doesn't accept a filename by default, so unknown files are assigned a file name based on the URL (which does not know the extension to give based on content type). If I force the file name by specifying it, I lose the ability for the browser to open the file directly and I get a download prompt. Has anyone else encountered this?

    These are the examples of what I've tried so far.

    //Gives me a download prompt.
    return File(document.Data, document.ContentType, document.Name);
    

    //Opens if it is a known extension type, downloads otherwise (download has bogus name and missing extension)
    return new FileStreamResult(new MemoryStream(document.Data), document.ContentType);
    

    //Gives me a download prompt (lose the ability to open by default if known type)
    return new FileStreamResult(new MemoryStream(document.Data), document.ContentType) {FileDownloadName = document.Name};
    

    Any suggestions?


    UPDATE: This questions seems to strike a chord with a lot of people, so I thought I'd post an update. The warning on the accepted answer below that was added by Oskar regarding international characters is completely valid, and I've hit it a few times due to using the ContentDisposition class. I've since updated my implementation to fix this. While the code below is from my most recent incarnation of this problem in an ASP.NET Core (Full Framework) app, it should work with minimal changes in an older MVC application as well since I'm using the System.Net.Http.Headers.ContentDispositionHeaderValue class.

    using System.Net.Http.Headers;
    
    public IActionResult Download()
    {
        Document document = ... //Obtain document from database context
    
        //"attachment" means always prompt the user to download
        //"inline" means let the browser try and handle it
        var cd = new ContentDispositionHeaderValue("attachment")
        {
            FileNameStar = document.FileName
        };
        Response.Headers.Add(HeaderNames.ContentDisposition, cd.ToString());
    
        return File(document.Data, document.ContentType);
    }
    
    // an entity class for the document in my database 
    public class Document
    {
        public string FileName { get; set; }
        public string ContentType { get; set; }
        public byte[] Data { get; set; }
        //Other properties left out for brevity
    }