Reputation: 12314
I am returning a file from a WebAPI controller. The Content-Disposition header value is automatically set to "attachment". For example:
Disposition: attachment; filename="30956.pdf"; filename*=UTF-8''30956.pdf
When it is set to attachment the browser will ask to save file instead of opening it. I would like it to open it.
How can I set it to "inline" instead of "attachment"?
I am sending the file using this method:
public IActionResult GetDocument(int id)
{
var filename = $"folder/{id}.pdf";
var fileContentResult = new FileContentResult(File.ReadAllBytes(filename), "application/pdf")
{
FileDownloadName = $"{id}.pdf"
};
// I need to delete file after me
System.IO.File.Delete(filename);
return fileContentResult;
}
Upvotes: 96
Views: 125608
Reputation: 1
NET CORE 8.0
public async Task<IActionResult> DownloadArchivoInforme(string archivo)
{
// Since this is just and example, I am using a local file located inside wwwroot
// Afterwards file is converted into a stream
var path = Path.Combine(_hostingEnvironment.WebRootPath + @"\Informes", archivo);
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
Response.Headers.Add("content-disposition", "inline;filename=" + $"{archivo}");
return File(fs, "application/pdf");//, $"{archivo}");
}
Upvotes: -1
Reputation: 1293
As of .NET 8 with Angular this worked for me (append on Headers):
var contentDisposition = new System.Net.Mime.ContentDisposition
{
FileName = download.FileName,
Inline = false
};
Response.Headers.Append("Content-Disposition", contentDisposition.ToString());
return File(download.FileBytes, download.ContentType);
Upvotes: 1
Reputation: 1060
Note that when the file can't be opened in the client's browser it will be downloaded. To assure filenames with special characters are correctly handled I found the following method to be most robust to set the Content-Disposition header:
var contentDisposition = new ContentDispositionHeaderValue("inline");
contentDisposition.SetHttpFileName("éáëí.docx");
Response.Headers.Add(HeaderNames.ContentDisposition, contentDisposition.ToString());
ContentDispositionHeaderValue is located in namespace Microsoft.Net.Http.Headers.
Upvotes: 1
Reputation: 4295
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
Update
In .NET 6, set the Content-Disposition
header to inline
or attachment
by adding it to the response header:
// inline
Response.Headers.Add("Content-Disposition", "inline");
return File(fileStream, contentType);
// attachment
Response.Headers.Add("Content-Disposition", "attachment;filename=some.txt");
return File(fileStream, contentType);
Upvotes: 108
Reputation: 449
This simply works for me in asp.net core 5.0 and hopefully this will work for previous versions too, as I was using same in asp.net 4.8
Response.ContentType = "application/pdf";
Response.Headers.Add("pragma", "no-cache, public");
Response.Headers.Add("cache-control", "private, nocache, must-revalidate, maxage=3600");
Response.Headers.Add("content-disposition", "inline;filename=" + fileName);
return File(bytes, "application/pdf");
Upvotes: 1
Reputation: 764
For ASP.NET Core, there doesn't seem to be any built-in way to return a file with 'Content-Disposition: inline' and filename. I created the following helper class that works very well. Tested with .NET Core 2.1.
public class InlineFileActionResult : Microsoft.AspNetCore.Mvc.IActionResult
{
private readonly Stream _stream;
private readonly string _fileName;
private readonly string _contentType;
private readonly int _bufferSize;
public InlineFileActionResult(Stream stream, string fileName, string contentType,
int bufferSize = DefaultBufferSize)
{
_stream = stream ?? throw new ArgumentNullException(nameof(stream));
_fileName = fileName ?? throw new ArgumentNullException(nameof(fileName));
_contentType = contentType ?? throw new ArgumentNullException(nameof(contentType));
if (bufferSize <= 0)
throw new ArgumentOutOfRangeException(nameof(bufferSize), bufferSize,
"Buffer size must be greater than 0");
_bufferSize = bufferSize;
}
public async Task ExecuteResultAsync(Microsoft.AspNetCore.Mvc.ActionContext context)
{
using (_stream)
{
var response = context.HttpContext.Response;
response.Headers[HeaderNames.ContentType] = _contentType;
response.Headers[HeaderNames.ContentLength] = _stream.Length.ToString();
response.Headers[HeaderNames.ContentDisposition] =
new Microsoft.Net.Http.Headers.ContentDispositionHeaderValue(
System.Net.Mime.DispositionTypeNames.Inline) {FileName = _fileName}.ToString();
await _stream.CopyToAsync(response.Body, _bufferSize, context.HttpContext.RequestAborted);
}
}
public const int DefaultBufferSize = 81920;
}
To use, return the class from the controller (whose return method must be IActionResult). An example is shown below:
[HttpGet]
public IActionResult Index()
{
var filepath = "C:\Path\To\Document.pdf";
return new InlineFileActionResult(new FileStream(filepath, FileMode.Open),
Path.GetFileName(filepath), "application/pdf");
}
Upvotes: 2
Reputation: 28091
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.
Upvotes: 19
Reputation: 854
None of these solutions worked for me. The only thing that worked for me was updating the Cors of the backend:
services.AddCors(o => o.AddPolicy("MyPolicy", b =>
{
b.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.WithExposedHeaders("Content-Disposition");
}));
so the header would be exposed. After this, I didn't need to add any additional header to the response.
And If you don't want to update your Startup.cs you can allow the header manually for that response:
HttpContext.Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
HttpContext.Response.Headers.Add("Content-Disposition", <your_header_value>);
Upvotes: 7
Reputation: 1265
Try this code in classic Razor page (tested in ASP.NET Core 3.1). For forced download is used query param "?download=1". As you see, necessary is add parameter "attachment" into the "Content-Disposition" header for the specific position.
public class FilesModel : PageModel
{
IWebHostEnvironment environment;
public FilesModel(IWebHostEnvironment environment)
{
this.environment = environment;
}
public PhysicalFileResult OnGet()
{
// Query params
string fileName = Request.Query["filename"];
bool forcedDownload = Request.Query["download"] == "1";
// File Path
string filePath = Path.Combine(env.ContentRootPath, "secret-files", fileName);
if (!System.IO.File.Exists(filePath)) return null; // File not exists
// Make sure that the user has permissions on the file...
// File info
string mime = "image/png"; // Choose the right mime type...
long fileSize = new FileInfo(filePath).Length;
string sendType = forcedDownload ? "attachment" : "inline";
// Headers
Response.Headers.Add("Content-Disposition", $"{sendType};filename=\"{fileName}\"");
Response.Headers.Add("Content-Length", fileSize.ToString());
Response.Headers.Add("X-Content-Type-Options", "nosniff");
// Result
return new PhysicalFileResult(filePath, mime);
}
}
Upvotes: -2
Reputation: 335
I followed @myro's answer. For my .net core 3.1 web API, I found the ContentDisposition class and constants in the System.Net.Mime namespace.
var result = new FileContentResult(System.IO.File.ReadAllBytes(filePath), mimeType);
var dispositionType = asAttachment
? System.Net.Mime.DispositionTypeNames.Attachment
: System.Net.Mime.DispositionTypeNames.Inline;
Response.Headers[HeaderNames.ContentDisposition] = new
System.Net.Mime.ContentDisposition { FileName = "file.text",
DispositionType = dispositionType }.ToString();
return result;
Upvotes: -1
Reputation: 2723
Based on Ashley Lee's response but using ASP.Net Core stuff which solve problems for some file name patterns. Note that inline is the default content-disposition, so if you don't need to specify the filename (will be suggested if the user hit save on his browser) you can simply omit the content-disposition as suggested by Jonathan Wilson.
private IActionResult GetFile(int id)
{
var file = $"folder/{id}.pdf";
// Response...
var cd = new ContentDispositionHeaderValue("inline");
cd.SetHttpFileName(file);
Response.Headers[HeaderNames.ContentDisposition] = cd.ToString();
Response.Headers.Add("X-Content-Type-Options", "nosniff");
return File(System.IO.File.ReadAllBytes(file), "application/pdf");
}
Upvotes: 4
Reputation: 4520
An Asp.Net MVC approach using a similar approach to @ashley-lee
Note: Chrome downloads the attachment. See Ctrl-J list. But, if the user chooses 'Open' it will open 'in browser', a user would have to choose 'Open in System Viewer'. For example PDF signature fields are not visible in Browser based PDF viewers.
[HttpGet]
public ActionResult GenericForm()
{
return new DownloadFileAsAttachmentResult(@"GenericForm.pdf", @"\Content\files\GenericForm.pdf", "application/pdf");
}
public class DownloadFileAsAttachmentResult : ActionResult
{
private string _filenameWithExtension { get; set; }
private string _filePath { get; set; }
private string _contentType { get; set; }
// false = prompt the user for downloading; true = browser to try to show the file inline
private const bool DisplayInline = false;
public DownloadFileAsAttachmentResult(string FilenameWithExtension, string FilePath, string ContentType)
{
_filenameWithExtension = FilenameWithExtension;
_filePath = FilePath;
_contentType = ContentType;
}
public override void ExecuteResult(ControllerContext context)
{
HttpResponseBase response = context.HttpContext.Response;
response.Buffer = false;
response.ContentType = _contentType;
response.AddHeader("Content-Disposition", "attachment; filename=" + _filenameWithExtension); // force download
response.AddHeader("X-Content-Type-Options", "nosniff");
response.TransmitFile(_filePath);
}
}
Upvotes: 0
Reputation: 1196
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 :-)
Upvotes: 7
Reputation: 151586
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");
Upvotes: 17
Reputation: 3986
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");
}
Upvotes: 110
Reputation: 5075
try it with HttpResponseMessage
public IActionResult GetDocument(int id)
{
var filename = $"folder/{id}.pdf";
Response.Headers["Content-Disposition"] = $"inline; filename={id}.pdf";
var fileContentResult = new FileContentResult(System.IO.File.ReadAllBytes(filename), "application/pdf")
{
FileDownloadName = $"{id}.pdf"
};
// I need to delete file after me
System.IO.File.Delete(filename);
return fileContentResult;
}
Upvotes: 4