开发者

Stream file using ASP.NET MVC FileContentResult in a browser with a name?

Is there a way to stream a file using ASP.NET MVC FileContentResult within the browser with a specific name?

I have noticed that you can either have a FileDialog (Open/Save) or you ca开发者_如何学运维n stream the file in a browser window, but then it will use the ActionName when you try to save the file.

I have the following scenario:

byte[] contents = DocumentServiceInstance.CreateDocument(orderId, EPrintTypes.Quote);
result = File(contents, "application/pdf", String.Format("Quote{0}.pdf", orderId));

When I use this, I can stream the bytes, but a OPEN/SAVE file dialog is given to the user. I would like to actually stream this file in a browser window.

If I just use the FilePathResult, it shows the file in a browser window, but then when I click on "Save" button to save the file in PDF, it shows me the Action Name as the name of the file.

Has anyone encountered this?


public ActionResult Index()
{
    byte[] contents = FetchPdfBytes();
    return File(contents, "application/pdf", "test.pdf");
}

and for opening the PDF inside the browser you will need to set the Content-Disposition header:

public ActionResult Index()
{
    byte[] contents = FetchPdfBytes();
    Response.AddHeader("Content-Disposition", "inline; filename=test.pdf");
    return File(contents, "application/pdf");
}


Actually, the absolutely easiest way is to do the following...

byte[] content = your_byte[];

FileContentResult result = new FileContentResult(content, "application/octet-stream") 
{
  FileDownloadName = "your_file_name"
};

return result;


This might be helpful for whoever else faces this problem. I finally figured out a solution. Turns out, even if we use the inline for "content-disposition" and specify a file name, the browsers still do not use the file name. Instead browsers try and interpret the file name based on the Path/URL.

You can read further on this URL: Securly download file inside browser with correct filename

This gave me an idea, I just created my URL route that would convert the URL and end it with the name of the file I wanted to give the file. So for e.g. my original controller call just consisted of passing the Order Id of the Order being printed. I was expecting the file name to be of the format Order{0}.pdf where {0} is the Order Id. Similarly for quotes, I wanted Quote{0}.pdf.

In my controller, I just went ahead and added an additional parameter to accept the file name. I passed the filename as a parameter in the URL.Action method.

I then created a new route that would map that URL to the format: http://localhost/ShoppingCart/PrintQuote/1054/Quote1054.pdf


routes.MapRoute("", "{controller}/{action}/{orderId}/{fileName}",
                new { controller = "ShoppingCart", action = "PrintQuote" }
                , new string[] { "x.x.x.Controllers" }
            );

This pretty much solved my issue.


Previous answers are correct: adding the line...

Response.AddHeader("Content-Disposition", "inline; filename=[filename]");

...will causing multiple Content-Disposition headers to be sent down to the browser. This happens b/c FileContentResult internally applies the header if you supply it with a file name. An alternative, and pretty simple, solution is to simply create a subclass of FileContentResult and override its ExecuteResult() method. Here's an example that instantiates an instance of the System.Net.Mime.ContentDisposition class (the same object used in the internal FileContentResult implementation) and passes it into the new class:

public class FileContentResultWithContentDisposition : FileContentResult
{
    private const string ContentDispositionHeaderName = "Content-Disposition";

    public FileContentResultWithContentDisposition(byte[] fileContents, string contentType, ContentDisposition contentDisposition)
        : base(fileContents, contentType)
    {
        // check for null or invalid ctor arguments
        ContentDisposition = contentDisposition;
    }

    public ContentDisposition ContentDisposition { get; private set; }

    public override void ExecuteResult(ControllerContext context)
    {
        // check for null or invalid method argument
        ContentDisposition.FileName = ContentDisposition.FileName ?? FileDownloadName;
        var response = context.HttpContext.Response;
        response.ContentType = ContentType;
        response.AddHeader(ContentDispositionHeaderName, ContentDisposition.ToString());
        WriteFile(response);
    }
}

In your Controller, or in a base Controller, you can write a simple helper to instantiate a FileContentResultWithContentDisposition and then call it from your action method, like so:

protected virtual FileContentResult File(byte[] fileContents, string contentType, ContentDisposition contentDisposition)
{
    var result = new FileContentResultWithContentDisposition(fileContents, contentType, contentDisposition);
    return result;
}

public ActionResult Report()
{
    // get a reference to your document or file
    // in this example the report exposes properties for
    // the byte[] data and content-type of the document
    var report = ...
    return File(report.Data, report.ContentType, new ContentDisposition {
        Inline = true,
        FileName = report.FileName
    });
}

Now the file will be sent to the browser with the file name you choose and with a content-disposition header of "inline; filename=[filename]".

I hope that helps!


The absolute easiest way to stream a file into browser using ASP.NET MVC is this:

public ActionResult DownloadFile() {
    return File(@"c:\path\to\somefile.pdf", "application/pdf", "Your Filename.pdf");
}

This is easier than the method suggested by @azarc3 since you don't even need to read the bytes.

Credit goes to: http://prideparrot.com/blog/archive/2012/8/uploading_and_returning_files#how_to_return_a_file_as_response

** Edit **

Apparently my 'answer' is the same as the OP's question. But I am not facing the problem he is having. Probably this was an issue with older version of ASP.NET MVC?


I adapted it in ASP.NET Core with REST API.

public class FileContentWithFileNameResult : FileContentResult
{
    public FileContentWithFileNameResult(byte[] fileContents, string contentType, string fileName)
   : base(fileContents, contentType)
    {
        FileName = fileName;
    }

    public string FileName { get; private set; }

    public override Task ExecuteResultAsync(ActionContext context)
    {
        var response = context.HttpContext.Response;  
        response.Headers.Append("Content-Disposition", $"inline; filename={FileName}");
        response.Headers.Append("Access-Control-Expose-Headers", "Content-Disposition");
        response.Headers.Append("X-Content-Type-Options", "nosniff");
        return base.ExecuteResultAsync(context);
    }
}


public FileContentResult GetImage(int productId) { 
     Product prod = repository.Products.FirstOrDefault(p => p.ProductID == productId); 
     if (prod != null) { 
         return File(prod.ImageData, prod.ImageMimeType); 
      } else { 
         return null; 
     } 
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜