Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/337.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# ASP.Net核心内容处置附件/内联_C#_Asp.net Core - Fatal编程技术网

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

C# ASP.Net核心内容处置附件/内联,c#,asp.net-core,C#,Asp.net Core,我正在从WebAPI控制器返回一个文件。内容处置标题值自动设置为“附件”。例如: 性格:依恋;filename=“30956.pdf”;文件名*=UTF-8''30956.pdf 当设置为“附件”时,浏览器将要求保存文件而不是打开文件。我想把它打开 如何将其设置为“内联”而不是“附件” 我正在使用以下方法发送文件: public IActionResult GetDocument(int-id) { var filename=$“folder/{id}.pdf”; var fileContent

我正在从WebAPI控制器返回一个文件。内容处置标题值自动设置为“附件”。例如:

性格:依恋;filename=“30956.pdf”;文件名*=UTF-8''30956.pdf

当设置为“附件”时,浏览器将要求保存文件而不是打开文件。我想把它打开

如何将其设置为“内联”而不是“附件”

我正在使用以下方法发送文件:

public IActionResult GetDocument(int-id)
{
var filename=$“folder/{id}.pdf”;
var fileContentResult=newfilecontentresult(File.ReadAllBytes(文件名),“application/pdf”)
{
FileDownloadName=$“{id}.pdf”
};
//我需要在我之后删除文件
System.IO.File.Delete(文件名);
返回fileContentResult;
}

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

我发现最好的方法是手动添加内容处置头

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

如果您不想在字节数组中一次性读取内存中的文件(使用各种
文件(字节[…)
重载或使用
FileContentResult
),您可以使用
文件(流、字符串、字符串)
重载,其中,最后一个参数表示文件的下载名称:

return File(stream, "content/type", "FileDownloadName.ext");
或者,您可以利用支持流式传输的现有响应类型,例如,并自行设置内容配置。如中所示,实现这一点的规范方法是在您的操作方法中自己在响应上设置头:

// 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");
由于
File()
将忽略
内容处置
我使用了以下方法:

Response.Headers[HeaderNames.ContentDisposition] = new MimeKit.ContentDisposition { FileName = fileName, Disposition = MimeKit.ContentDisposition.Inline }.ToString();
return new FileContentResult(System.IO.File.ReadAllBytes(filePath), "application/pdf");

它可以工作:-)

对于
AspNetCore
AspNetCore.Mvc
的2.0.0版,我发现前面的答案都不可接受。对我来说,只需将filename参数匹配到
File
就足以触发内联内容处理

return File(fileStream, contentType, fileName); // attachment
return File(fileStream, contentType);           // inline
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);
    }
}

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

您可以覆盖默认的
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
。F.e.:

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


正如所指出的,不要使用
ContentDisposition
类来生成头值,因为它将在头值中插入新行以获得更长的文件名。

基于Ashley Lee的回答,但使用ASP.Net核心技术可以解决某些文件名模式的问题。请注意,内联是默认的内容配置,因此,如果您不需要指定文件名(如果用户在浏览器上单击“保存”,则建议您这样做),您只需按照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");
}
我听从了他的回答。对于我的.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”。如您所见,需要将参数“附件”添加到特定位置的“内容处置”标题中

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

对于ASP.NETCore,似乎没有任何内置的方法返回带有“contentdisposition:inline”和文件名的文件。我创建了下面的helper类,它工作得非常好。使用.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;
}

这些解决方案都不适合我。唯一对我有用的是更新后端的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>);
HttpContext.Response.Headers.Add(“访问控制公开头”,“内容处置”);
HttpContext.Response.Headers.Add(“内容处置”);

我需要ASP.Net核心,afaic这将返回一个ASP.Net对象。尝试后,它返回JSON序列化的HttpResponseMessage对象。我创建了以下问题:要跟踪此问题,您也不需要自己读取字节,您可以执行
返回文件(System.IO.File.OpenRead(“完整文件路径”),contentType:“application/pdf”)我返回这个,
返回文件(System.IO.File.ReadAllBytes(“C:/Temp/Generic.csv”),“application/octet-stream”,“Generic.csv”)
但绝对没有发生任何事情。请快速检查是否有人试图使其正常工作:确保在构造
FileStreamResult
时未传递
fileDownloadName
参数,否则它将覆盖自定义的“内容处置”标题!当文件名以D开头并且是pdf时,我个人对此有一些问题。我建议使用aspnet核心类在aspnet核心中执行此操作:返回FileContentResult或使用ContentDispositionHeaderValue设置内容处置请勿使用
ContentDisposition.ToString()
!!!如果包含一个特殊字符,则所有字符都将进行Base64编码,并在每42个字符块的新行中拆分,例如,
“1234567890123456789012345789012345678ä.pdf”
→ <代码>“=?utf-8?B?MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTc4OTAxMjM0NTY3OMOkLnBk?=\r\n=?utf-8?B?Zg=?=”
并在
响应中添加新行。标题
→ System.InvalidOperationException:'标头中的控制字符无效:0x0D'注意,您可以以强类型方式指定标头:
Response.GetTypedHeaders().lastModifier