![](/img/trans.png)
[英]Get a specific response header (e.g., Content-Disposition) in React from an ASP.NET CORE Web Api
[英]ASP.Net Core Content-Disposition attachment/inline
我正在从 WebAPI 控制器返回一个文件。 Content-Disposition 标头值自动设置为“附件”。 例如:
处置:附件; 文件名="30956.pdf"; 文件名*=UTF-8''30956.pdf
当它设置为附件时,浏览器将要求保存文件而不是打开它。 我想打开它。
如何将其设置为“内联”而不是“附件”?
我正在使用这种方法发送文件:
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;
}
我发现的最好方法是手动添加内容处置标头。
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");
}
使用 2.0.0 版的AspNetCore
和AspNetCore.Mvc
,我发现以前的答案都不能接受。 对我来说,只需将 filename 参数省略给File
就足以触发内联内容处置。
return File(fileStream, contentType, fileName); // attachment
return File(fileStream, contentType); // inline
鉴于您不想在字节数组中一次读取内存中的文件(使用各种File(byte[]...)
重载或使用FileContentResult
),您可以使用File(Stream, string, string)
重载,其中最后一个参数表示文件将用于下载的名称:
return File(stream, "content/type", "FileDownloadName.ext");
或者您可以利用支持流的现有响应类型,例如FileStreamResult
,并自己设置内容处置。 执行此操作的规范方法,如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");
您可以覆盖默认的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);
}
}
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);
}
}
而不是返回FileContentResult
或FileStreamResult
,只需返回InlineFileContentResult
或InlineFileStreamResult
。 铁:
public IActionResult GetDocument(int id)
{
var filename = $"folder/{id}.pdf";
return new InlineFileContentResult(File.ReadAllBytes(filename), "application/pdf")
{
FileDownloadName = $"{id}.pdf"
};
}
警告
正如makman99所指出的,不要使用ContentDisposition
类来生成标头值,因为它会在标头值中插入换行符以获得更长的文件名。
由于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");
它有效:-)
用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;
}
基于 Ashley Lee 的回应,但使用 ASP.Net Core 的东西来解决某些文件名模式的问题。 请注意,内联是默认的内容处理,因此如果您不需要指定文件名(如果用户在浏览器上点击保存将被建议),您可以简单地省略 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");
}
这些解决方案都不适合我。 唯一对我有用的是更新后端的 Cors:
services.AddCors(o => o.AddPolicy("MyPolicy", b =>
{
b.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.WithExposedHeaders("Content-Disposition");
}));
所以标题会被暴露。 在此之后,我不需要向响应添加任何额外的标头。
如果您不想更新 Startup.cs,您可以手动允许该响应的标头:
HttpContext.Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
HttpContext.Response.Headers.Add("Content-Disposition", <your_header_value>);
使用与@ashley-lee 类似的方法的 Asp.Net MVC 方法
注意: Chrome 会下载附件。 请参阅 Ctrl-J 列表。 但是,如果用户选择“打开”,它将在“浏览器中”打开,用户必须选择“在系统查看器中打开”。 例如,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);
}
}
对于 ASP.NET Core,似乎没有任何内置方法可以返回带有“Content-Disposition: inline”和文件名的文件。 我创建了以下非常有效的帮助程序类。 使用 .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;
}
要使用,从控制器返回类(其返回方法必须是 IActionResult)。 一个例子如下所示:
[HttpGet]
public IActionResult Index()
{
var filepath = "C:\Path\To\Document.pdf";
return new InlineFileActionResult(new FileStream(filepath, FileMode.Open),
Path.GetFileName(filepath), "application/pdf");
}
这在asp.net core 5.0中对我有用,希望这也适用于以前的版本,因为我在asp.net 4.8中使用相同
Response.ContentType = "申请/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);
返回文件(字节,“应用程序/pdf”);
我跟着@myro 的回答。 对于我的 .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;
在经典的 Razor 页面中尝试此代码(在 ASP.NET Core 3.1 中测试)。 对于强制下载,使用查询参数“?download=1”。 如您所见,必须将参数“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.