【问题标题】:Correct way of streaming a CSV of unknown size from memory从内存中流式传输未知大小的 CSV 的正确方法
【发布时间】:2018-05-13 18:19:25
【问题描述】:

我有一个数据库,其中有一个表,其中包含大约 20 列的超过 200 万条记录。用户可以查询数据库并限制返回的记录数,因此记录集可能在 1 到 200 万之间。

由于它是表格信息,我想以 CSV 格式发送数据。我正在使用 StreamWriter 将数据写入内存,一旦文件完成,我会将其作为 HttpResponseMessage 发送。我的代码在下面,只要我没有耗尽内存,它就可以正常工作。有没有办法让我在处理文件时对其进行流式传输,以使使用的内存最少?

<HttpGet, Route("api/records/export")>
Public Function ExportRecords() As HttpResponseMessage

    Dim stream As New MemoryStream
    Dim writer As New StreamWriter(stream)
    writer.WriteLine("")

    ' Processing of data here
    writer.WriteLine("""Write Data to MemoryStream"")

    writer.Flush()
    stream.Position = 0

    Dim result As New HttpResponseMessage(HttpStatusCode.OK)
    result.Content = New StreamContent(stream)
    result.Content.Headers.ContentType = New Headers.MediaTypeHeaderValue("text/csv")
    result.Content.Headers.ContentDisposition = New Headers.ContentDispositionHeaderValue("attachment") _ 
        With {.FileName = "I" & Format(Date.Now, "yyMMdd") & ".csv"}

    Return result

End Function

我在 StackOverflow 上阅读了对 Returning binary file from controller in ASP.NET Web API 等问题的回答,但这些都涉及从存储在磁盘上的文件而不是从内存中流式传输 Web 响应。

【问题讨论】:

  • 你可以只写到 Response.OutputStream 并跳过内存流。
  • 您可以使用链接示例中的 PushStreamContent,您只需将 MemorySteam 替换为 PushStreamContent 中的流参数并省略 stream.Position = 0 语句。它会是这样的(请原谅我在这里使用 C#):result.Content = new PushStreamContent((stream, httpContent, transportContext) =&gt; { var writer = new StreamWriter(stream); \* Rest of your code *\ writer.Flush(); stream.Close(); })m "text/csv");

标签: asp.net vb.net csv asp.net-web-api streamwriter


【解决方案1】:

正如对我的问题的评论所建议的那样,我使用PushStreamContent 将我的数据库内容以 CSV 格式流式传输到浏览器。我还将其设为异步以从导出中获得更多性能。

使用PushStreamContent 将内容从服务器流式传输到客户端有一个重要限制。由于200 OK 标头是在任何内容流式传输之前首先发送的,因此响应不会提前知道在返回结果时是否会出现错误。如果在发送结果时出现问题,客户端只会看到一个通用的网络错误。由服务器记录任何错误,以便您可以检查服务器日志以查找特定错误。

这是我使用的PushStreamContent 的代码(为简洁起见,删除了错误检查)。

Dim result As HttpResponseMessage = New HttpResponseMessage(HttpStatusCode.OK) With {
    .Content = New PushStreamContent(Async Function(outStream, httpContent, context)
        Dim writer As StreamWriter = New StreamWriter(outStream)
        Await writer.WriteLineAsync("""Header 1"",""Header 2"",""Header 3""")
        For Each item In returnItems
            Await writer.WriteLineAsync("""" & item.Col1.ToString & """,=""" & item.Col2.ToString & """,=""" & item.Col3.ToString & """")
            Await writer.FlushAsync()
        Next
        outputStream.Close()
    End Function)}

result.Content.Headers.ContentType = New Headers.MediaTypeHeaderValue("text/csv")
result.Content.Headers.ContentDisposition = New _
  Headers.ContentDispositionHeaderValue("attachment") With {.FileName = "MyCSV.csv"}

Return result

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-31
    • 1970-01-01
    • 1970-01-01
    • 2020-02-29
    相关资源
    最近更新 更多