[英]ASP.Net Core Content-Disposition attachment/inline
I am returning a file from a WebAPI controller.我正在从 WebAPI 控制器返回一个文件。 The Content-Disposition header value is automatically set to "attachment".
Content-Disposition 标头值自动设置为“附件”。 For example:
例如:
Disposition: attachment;
处置:附件; filename="30956.pdf";
文件名="30956.pdf"; filename*=UTF-8''30956.pdf
文件名*=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;
}
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.使用 2.0.0 版的
AspNetCore
和AspNetCore.Mvc
,我发现以前的答案都不能接受。 For me, simply ommitting the filename argument to File
was enough to trigger an inline content disposition.对我来说,只需将 filename 参数省略给
File
就足以触发内联内容处置。
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:鉴于您不想在字节数组中一次读取内存中的文件(使用各种
File(byte[]...)
重载或使用FileContentResult
),您可以使用File(Stream, string, string)
重载,其中最后一个参数表示文件将用于下载的名称:
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.或者您可以利用支持流的现有响应类型,例如
FileStreamResult
,并自己设置内容处置。 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:执行此操作的规范方法,如
FileResultExecutorBase
,是在您的操作方法中自己在响应中设置标头:
// 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:您可以覆盖默认的
FileContentResult
类,以便您可以在代码中使用它,只需进行最少的更改:
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
: 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
.而不是返回
FileContentResult
或FileStreamResult
,只需返回InlineFileContentResult
或InlineFileStreamResult
。 Fe:铁:
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.正如makman99所指出的,不要使用
ContentDisposition
类来生成标头值,因为它会在标头值中插入换行符以获得更长的文件名。
As File()
would ignore Content-Disposition
I used this:由于
File()
会忽略Content-Disposition
我使用了这个:
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 :-)它有效:-)
try it with HttpResponseMessage
用
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;
}
Based on Ashley Lee's response but using ASP.Net Core stuff which solve problems for some file name patterns.基于 Ashley Lee 的回应,但使用 ASP.Net Core 的东西来解决某些文件名模式的问题。 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.
请注意,内联是默认的内容处理,因此如果您不需要指定文件名(如果用户在浏览器上点击保存将被建议),您可以简单地省略 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");
}
None of these solutions worked for me.这些解决方案都不适合我。 The only thing that worked for me was updating the Cors of the backend:
唯一对我有用的是更新后端的 Cors:
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:如果您不想更新 Startup.cs,您可以手动允许该响应的标头:
HttpContext.Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
HttpContext.Response.Headers.Add("Content-Disposition", <your_header_value>);
An Asp.Net MVC approach using a similar approach to @ashley-lee使用与@ashley-lee 类似的方法的 Asp.Net MVC 方法
Note: Chrome downloads the attachment.注意: Chrome 会下载附件。 See Ctrl-J list.
请参阅 Ctrl-J 列表。 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.
例如,PDF 签名字段在基于浏览器的 PDF 查看器中不可见。
[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);
}
}
For ASP.NET Core, there doesn't seem to be any built-in way to return a file with 'Content-Disposition: inline' and filename.对于 ASP.NET Core,似乎没有任何内置方法可以返回带有“Content-Disposition: inline”和文件名的文件。 I created the following helper class that works very well.
我创建了以下非常有效的帮助程序类。 Tested with .NET Core 2.1.
使用 .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).要使用,从控制器返回类(其返回方法必须是 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");
}
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这在asp.net core 5.0中对我有用,希望这也适用于以前的版本,因为我在asp.net 4.8中使用相同
Response.ContentType = "application/pdf"; Response.ContentType = "申请/pdf";
Response.Headers.Add("pragma", "no-cache, public"); Response.Headers.Add("pragma", "no-cache, public");
Response.Headers.Add("cache-control", "private, nocache, must-revalidate, maxage=3600"); Response.Headers.Add("cache-control", "private, nocache, must-revalidate, maxage=3600");
Response.Headers.Add("content-disposition", "inline;filename=" + fileName); Response.Headers.Add("content-disposition", "inline;filename=" + fileName);
return File(bytes, "application/pdf");返回文件(字节,“应用程序/pdf”);
I followed @myro's answer.我跟着@myro 的回答。 For my .net core 3.1 web API, I found the ContentDisposition class and constants in the System.Net.Mime namespace.
对于我的 .net core 3.1 web API,我在 System.Net.Mime 命名空间中找到了 ContentDisposition 类和常量。
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;
Try this code in classic Razor page (tested in ASP.NET Core 3.1).在经典的 Razor 页面中尝试此代码(在 ASP.NET Core 3.1 中测试)。 For forced download is used query param "?download=1".
对于强制下载,使用查询参数“?download=1”。 As you see, necessary is add parameter "attachment" into the "Content-Disposition" header for the specific position.
如您所见,必须将参数“attachment”添加到特定位置的“Content-Disposition”标头中。
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);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.