如何构建 csv 字符串并将其作为文件流式传输到客户端 (.net c#)

How to build and stream a csv string as a file to a client (.net c#)

我有以下方法来讨论 SomeMetric 模型的列表并将其转换为 csv 字符串。该方法将 csv 字符串作为文件写入 HttpResponseMessage:

private HttpResponseMessage ConvertToCsvFileResponse(IEnumerable<SomeMetric> filterRecords, string fileName)
{
    var csvBuilder = new StringBuilder();

    //write the header
    csvBuilder.AppendLine("ColA,ColB,ColC,ColD,ColE");

    // Write the data lines.
    foreach (var record in filterRecords)
        csvBuilder.AppendFormat("{0},{1},{2},{3},{4}{5}", record.A, record.B, record.C, record.D, record.E, Environment.NewLine);

    // Convert to Http response message for outputting from api.
    HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
    result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
    result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); //attachment will force download
    result.Content.Headers.ContentDisposition.FileName = fileName;
    result.Content = new StringContent(csvBuilder.ToString());

    return result;
}

这个方法是从我的 API 控制器调用的,响应输出到客户端如下:

[HttpGet]
public HttpResponseMessage GetExampleCsv()
{
    try
    {
        var entries = _auditTableStorage.ListEntities<SomeMetric>();

        return ConvertToCsvFileResponse(entries.ToList(), "Example.csv");
    }
    catch (Exception ex)
    {
        return new HttpResponseMessage(HttpStatusCode.InternalServerError) {
            Content = new StringContent($"Problem occurred connecting to storage: {ex.Message}")
        };
    }
}

我通过查询 Azure Table 存储生成数据 - 但这与我的问题无关。

数据来自分页格式的数据源。我目前的做法是首先将所有数据收集在一起(分页直到结束)以生成一个列表(返回 IEnumerable 并调用 .ToList();)。然后将此数据传递给 ConvertToCsv 方法。

正如您所想象的,以这种方式预先构建数据集对于中小型数据集是可行的,但对于大型数据集来说很快就会变得低效。

理想情况下,我想修改我的代码以获取正在构建的 CSV 字符串块,并将其作为文件流式传输到发出请求的客户端 - 只是不确定如何将我拥有的内容转换为流媒体版本。非常感谢任何积分!

注意:此处生成csv的代码仅用于演示目的。我可以将对象集成到 CSV 解析器,但想显示字符串生成并在发生时将其删除。

对于希望获得这方面示例的任何其他人,以下是我如何在构建客户端浏览器时将内容流式传输到客户端浏览器:

private HttpResponseMessage ConvertToCsvFileResponse(IEnumerable<SomeMetric> filterRecords, string fileName)
{
    HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
    result.Content = new PushStreamContent((outputStream, httpContext, transportContext) =>
    {
        using (StreamWriter sWriter = new StreamWriter(outputStream, UnicodeEncoding.ASCII))
        {
            sWriter.Write("A,B,C,D,E");

            foreach (var record in filterRecords)
                sWriter.Write($"{Environment.NewLine}{record.A},{record.B},{record.C},{record.D},{record.E}");
        }

    });
    result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
    result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); 
    result.Content.Headers.ContentDisposition.FileName = fileName;

    return result;
}