ASP.Net Core Content-Disposition attachment/inline
The best way I have found is to add the content-disposition headers manually.
private IActionResult GetFile(int id)
{
var file = $"folder/{id}.pdf";
// Response...
System.Net.Mime.ContentDisposition cd = new System.Net.Mime.ContentDisposition
{
FileName = file,
Inline = displayInline // false = prompt the user for downloading; true = browser to try to show the file inline
};
Response.Headers.Add("Content-Disposition", cd.ToString());
Response.Headers.Add("X-Content-Type-Options", "nosniff");
return File(System.IO.File.ReadAllBytes(file), "application/pdf");
}
With version 2.0.0 of AspNetCore
and AspNetCore.Mvc
, I found none of the previous answers to be acceptable. For me, simply ommitting the filename argument to File
was enough to trigger an inline content disposition.
return File(fileStream, contentType, fileName); // attachment
return File(fileStream, contentType); // inline
Given you don't want to read the file in memory at once in a byte array (using the various File(byte[]...)
overloads or using FileContentResult
), you can either use the File(Stream, string, string)
overload, where the last parameter indicates the name under which the file will be presented for download:
return File(stream, "content/type", "FileDownloadName.ext");
Or you can leverage an existing response type that supports streaming, such as a FileStreamResult
, and set the content-disposition yourself. The canonical way to do this, as demonstrated in the FileResultExecutorBase
, is to simply set the header yourself on the response, in your action method:
// Set up the content-disposition header with proper encoding of the filename
var contentDisposition = new ContentDispositionHeaderValue("attachment");
contentDisposition.SetHttpFileName("FileDownloadName.ext");
Response.Headers[HeaderNames.ContentDisposition] = contentDisposition.ToString();
// Return the actual filestream
return new FileStreamResult(@"path\to\file", "content/type");
You can override the default FileContentResult
class so you can use it in your code with minimal changes:
public class InlineFileContentResult : FileContentResult
{
public InlineFileContentResult(byte[] fileContents, string contentType)
: base(fileContents, contentType)
{
}
public override Task ExecuteResultAsync(ActionContext context)
{
var contentDispositionHeader = new ContentDispositionHeaderValue("inline");
contentDispositionHeader.SetHttpFileName(FileDownloadName);
context.HttpContext.Response.Headers.Add(HeaderNames.ContentDisposition, contentDispositionHeader.ToString());
FileDownloadName = null;
return base.ExecuteResultAsync(context);
}
}
The same can be done for the FileStreamResult
:
public class InlineFileStreamResult : FileStreamResult
{
public InlineFileStreamResult(Stream fileStream, string contentType)
: base(fileStream, contentType)
{
}
public override Task ExecuteResultAsync(ActionContext context)
{
var contentDispositionHeader = new ContentDispositionHeaderValue("inline");
contentDispositionHeader.SetHttpFileName(FileDownloadName);
context.HttpContext.Response.Headers.Add(HeaderNames.ContentDisposition, contentDispositionHeader.ToString());
FileDownloadName = null;
return base.ExecuteResultAsync(context);
}
}
Instead of returning a FileContentResult
or FileStreamResult
, just return InlineFileContentResult
or InlineFileStreamResult
. F.e.:
public IActionResult GetDocument(int id)
{
var filename = $"folder/{id}.pdf";
return new InlineFileContentResult(File.ReadAllBytes(filename), "application/pdf")
{
FileDownloadName = $"{id}.pdf"
};
}
Warning
As pointed out by makman99, do not use the ContentDisposition
class for generating the header value as it will insert new-lines in the header-value for longer filenames.
As File()
would ignore Content-Disposition
I used this:
Response.Headers[HeaderNames.ContentDisposition] = new MimeKit.ContentDisposition { FileName = fileName, Disposition = MimeKit.ContentDisposition.Inline }.ToString();
return new FileContentResult(System.IO.File.ReadAllBytes(filePath), "application/pdf");
and it works :-)