c# - 使用OutputFormatterWriteContext.WriterFactory输出带有BOM的UTF-8

标签 c# asp.net-mvc asp.net-core-mvc

我需要从 TextOutputFormatter 的实现中返回一个包含非拉丁字符的 Excel 友好 csv 文件。以下代码显示了要点:

public class CsvOutputFormatter : TextOutputFormatter
{
    private readonly UTF8Encoding _encoding;

    public CsvOutputFormatter()
    {
        _encoding = new UTF8Encoding(true);
        SupportedEncodings.Add(_encoding);
        SupportedMediaTypes.Add(Microsoft.Net.Http.Headers.MediaTypeHeaderValue.Parse("text/csv"));
    }

    public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
    {

        var response = context.HttpContext.Response;

        response.Headers.Add("Content-Disposition", $"attachment; filename=test.csv");

        response.ContentType = "text/csv";
        var preamble = _encoding.GetPreamble();
        response.Body.Write(preamble, 0, preamble.Length);
        // this works
        //using (var writer = new StreamWriter(response.Body, _encoding))
        // this doesn't work
        using (var writer = context.WriterFactory(response.Body, _encoding))
        {
            var csv = new CsvWriter(writer);
            csv.Configuration.HasHeaderRecord = true;
            csv.Configuration.QuoteAllFields = true;
            csv.WriteRecords((IEnumerable<object>)context.Object);
            await writer.FlushAsync();
        }
    }

我的主要问题是为什么使用OutputFormatterWriteContext.WriterFactory时不输出BOM?

附带问题:

  1. 使用 OutputFormatterWriteContext.WriterFactory 而不是常规的 StreamWriter(在本例中效果很好)有什么附加值?

  2. 有没有办法避免显式写入 BOM,例如有一个编写器自动调用 Encoding.GetPreamble() 吗?

  3. 我知道带有 BOM 的 UTF-8 是非标准的,但我想知道在这种情况下是否有办法避免它?

最佳答案

What's the added value of using OutputFormatterWriteContext.WriterFactory instead of a regular StreamWriter, which works just fine in this case?

实际上,您可以写信给HttpResponse.Body如果你愿意,可以直接。重点是,正如文档所述,不要使用 WriterFactory当你想写入二进制数据时。

ASP.NET Core 使用 HttpResponseStreamWriter 在幕后写入流(参见 MemoryPoolHttpResponseStreamWriterFactory ) 。此实现公开了几个与 StreamWriter 非常相似的方法。 。但是HttpResponseStreamWriter使用 ArrayPool<> 在引擎盖后面。据此document ,当频繁创建和销毁数组时,它应该提高性能

My main questions is why the BOM not output when using OutputFormatterWriteContext.WriterFactory?

那是因为 HttpResponseStreamWriter doesn't write BOM 完全:

/// <summary<
/// Writes to the  using the supplied .
/// It does not write the BOM and also does not close the stream.
/// </summary<
public class HttpResponseStreamWriter : TextWriter
{
    private const int MinBufferSize = 128;
    internal const int DefaultBufferSize = 16 * 1024;
    ...
}

Is there a way to avoid explicitly writing the BOM, e.g. have a writer calling Encoding.GetPreamble() automatically?

If you're using the built-in OutputFormatterWriteContext.WriterFactory, I believe the answer is YES. You need to write the BOM header by your self if you wish.


Lastly, you should not write headers within the WriteResponseBodyAsync() method. That's a duty of WriteResponseHeaders(ctx). It's better to move this codes to WriteResponseHeaders(OutputFormatterWriteContext ctx ):

    public override void WriteResponseHeaders(OutputFormatterWriteContext ctx )
    {
        var response = ctx.HttpContext.Response;
        response.Headers.Add("Content-Disposition", $"attachment; filename=test.csv");
        response.ContentType = "text/csv";
    }

    public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
    {
        var response = context.HttpContext.Response;
        var preamble = _encoding.GetPreamble();
        response.Body.Write(preamble, 0, preamble.Length);
        using (var writer = context.WriterFactory(response.Body, _encoding))
        {
            var csv = new CsvWriter(writer);
            csv.Configuration.HasHeaderRecord = true;
            csv.Configuration.QuoteAllFields =  true;
            csv.WriteRecords((IEnumerable<object>)context.Object);
            await writer.FlushAsync();
        }
    }

关于c# - 使用OutputFormatterWriteContext.WriterFactory输出带有BOM的UTF-8,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56249164/

相关文章:

c# - 标记从 API 返回的值

c# - 如何安全地分块转换 `ToBase64String`?

c# - 将 ID 号添加到 CSS 标签

c# - 将本地 ASP.NET 应用程序发布到 Azure 后无法登录

c# - 如何将 Microsoft.jQuery.Unobtrusive.Ajax 与 libman(库管理器)asp.net Core 2.1 一起使用?

sql-server - 出于什么设计原因,Asp.Net Core SessionKey 与 SessionId 不同?

c# - 为什么这个 switch on 类型的案例被认为是令人困惑的?

c# - 将应用程序图标和窗口图标设置为 Windows 7 友好图标

jquery - ASP.NET MVC 4 : cannot modify jQuery Unobtrusive Ajax

c# - 一对一 Entity Framework 关系