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

时间:2016-08-11 13:28:59

标签: c# asp.net-core

我从WebAPI控制器返回一个文件。 Content-Disposition标头值自动设置为“attachment”。例如:

  

处置:附件;文件名= “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;
}

13 个答案:

答案 0 :(得分:44)

我找到的最好方法是手动添加内容处理标题。

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");
}

答案 1 :(得分:9)

对于AspNetCoreAspNetCore.Mvc的2.0.0版本,我发现以前的答案都不可接受。对我来说,只需将File的filename参数省略就足以触发内联内容处置。

return File(fileStream, contentType, fileName); // attachment
return File(fileStream, contentType);           // inline

答案 2 :(得分:8)

鉴于您不希望在字节数组中一次读取内存中的文件(使用各种File(byte[]...)重载或使用FileContentResult),您可以使用File(Stream, string, string) overload,其中最后一个参数表示文件的下载名称:

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");

答案 3 :(得分:3)

由于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");

它有效: - )

答案 4 :(得分:1)

使用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;
}

答案 5 :(得分:1)

使用与@ 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);
    }
}

答案 6 :(得分:0)

您可以覆盖默认的FileContentResult类,以便可以在代码中使用它,而只需进行很少的更改:

public class InlineFileContentResult : FileContentResult
{
    public InlineFileContentResult(byte[] fileContents, string contentType)
        : base(fileContents, contentType)
    {
    }

    public override Task ExecuteResultAsync(ActionContext context)
    {
        var contentDispositionHeader = new ContentDisposition
        {
            FileName = FileDownloadName,
            Inline = true,
        };
        context.HttpContext.Response.Headers.Add("Content-Disposition", 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 ContentDisposition
        {
            FileName = FileDownloadName,
            Inline = true,
        };
        context.HttpContext.Response.Headers.Add("Content-Disposition", contentDispositionHeader.ToString());
        FileDownloadName = null;
        return base.ExecuteResultAsync(context);
    }
}

返回FileContentResultFileStreamResult而不是返回InlineFileContentResultInlineFileStreamResult。 F.e。:

public IActionResult GetDocument(int id)
{
    var filename = $"folder/{id}.pdf";
    return new InlineFileContentResult(File.ReadAllBytes(filename), "application/pdf")
    {
        FileDownloadName = $"{id}.pdf"
    };
}

答案 7 :(得分:0)

基于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");
}

答案 8 :(得分:0)

我遵循了@myro's的回答。对于我的.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;

答案 9 :(得分:0)

在经典的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);
    }
}

答案 10 :(得分:0)

对于ASP.NET Core 3.1,似乎没有任何内置方法可以返回带有“ Content-Disposition:inline”和文件名的文件。我创建了以下效果很好的帮助程序类。

public class InlineFileActionResult : IActionResult
{
    private readonly Stream _stream;
    private readonly string _filename;
    private readonly string _contentType;

    public InlineFileActionResult(Stream stream, string filename, string contentType)
    {
        _stream = stream;
        _filename = filename;
        _contentType = contentType;
    }

    public InlineFileActionResult(byte[] file, string filename, string contentType)
    : this(new MemoryStream(file), filename, contentType) {}

    public async Task ExecuteResultAsync(ActionContext context)
    {
        byte[] file;
        if (_stream is MemoryStream memoryStream) 
            file = memoryStream.ToArray();
        else
        {
            await using var ms = new MemoryStream();
            await _stream.CopyToAsync(ms);
            file = ms.ToArray();
        }
        context.HttpContext.Response.Headers[HeaderNames.ContentType] = _contentType;
        context.HttpContext.Response.Headers[HeaderNames.ContentLength] = file.Length.ToString();
        context.HttpContext.Response.Headers[HeaderNames.ContentDisposition] =
            $"{System.Net.Mime.DispositionTypeNames.Inline}; filename=\"{_filename}\";";
        await context.HttpContext.Response.Body.WriteAsync(file, 0, file.Length);
    }
}

要使用,请从控制器返回类(其返回方法必须为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");
}

答案 11 :(得分:0)

这些解决方案都不适合我。唯一对我有用的是更新后端的 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>);

答案 12 :(得分:0)

这在 asp.net core 5.0 中仅适用于我,希望这也适用于以前的版本,因为我在 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);

返回文件(字节,“应用程序/pdf”);