我需要从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?

附带问题:


使用OutputFormatterWriteContext.WriterFactory代替常规的StreamWriter的附加值是什么,在这种情况下可以正常工作吗?
有没有办法避免显式编写BOM,例如有作家自动调用Encoding.GetPreamble()吗?
我知道带有BOM的UTF-8是非标准的,我想知道在这种情况下是否有避免的方法?

最佳答案

使用OutputFormatterWriteContext.WriterFactory代替常规StreamWriter的附加值是什么,在这种情况下可以正常工作吗?


实际上,如果愿意,您可以直接写HttpResponse.Body。关键是,如文档所述,要写入二进制数据时不要使用WriterFactory

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


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


这是因为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;
    ...
}

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/

10-12 15:38