简体   繁体   English

ASP.Net 核心内容处理附件/内联

[英]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 版的AspNetCoreAspNetCore.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 .而不是返回FileContentResultFileStreamResult ,只需返回InlineFileContentResultInlineFileStreamResult 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 HttpResponseMessageHttpResponseMessage试试

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.

相关问题 从 ASP.NET CORE Web Api 获取 React 中的特定响应标头(例如,Content-Disposition) - Get a specific response header (e.g., Content-Disposition) in React from an ASP.NET CORE Web Api C#ASP.NET 3.5内容处理文件下载问题 - C# ASP.NET 3.5 content-disposition file download problems .NET-WebAPI中未显示内容处置标头 - .NET - Content-Disposition Header Not Showing in WebAPI “内容处理”时MailKit.Net.Imap读取Gmail附件; 内联“已设置 - MailKit.Net.Imap read Gmail attachments when “Content-Disposition; inline” is set 使用Content-Disposition:attachment时XML节点被重写为小写 - XML nodes rewritten to lowercase when using Content-Disposition:attachment 从跨域 http.get 请求的 ASP.NET Web API 2 响应中获取 Angular 中的特定响应标头(例如,Content-Disposition) - Get a specific response header (e.g., Content-Disposition) in Angular from an ASP.NET Web API 2 response for a cross-origin http.get request Google Cloud Storage不会更新现有的默认Content-Disposition,而是使用.NET Client Libraries创建新的Content-Disposition元数据 - Google Cloud Storage not updating the existing default Content-Disposition but creating a new Content-Disposition metadata with .NET Client Libraries 内容处置始终为 null - Content-Disposition is always null Content-Disposition 标头中的 Unicode - Unicode in Content-Disposition header 获取 Content-Disposition 参数 - Get Content-Disposition parameters
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM