【问题标题】:How to async stream a file from database to webclient without buffering如何在不缓冲的情况下将文件从数据库异步流式传输到 webclient
【发布时间】:2013-11-27 01:43:10
【问题描述】:

我正在尝试使用 .net 4.5 和 web api 将文件从 sql server 异步流式传输到 web 客户端。

我正在使用 SqlDataReader.GetStream() 从数据库中获取流。但是,当 webapi 完成从流中读取时,我不确定如何在处置/关闭 db 连接中进行连接。

有样品吗?

【问题讨论】:

    标签: c# asp.net-web-api .net-4.5


    【解决方案1】:

    您可以在 Web API 中执行以下操作。这里我使用PushStreamContent 连接数据库并检索Sql 流。然后我将这个 sql 流直接复制到响应流中。在 Web API 中,当您使用 PushStreamContent 时,客户端将收到分块传输编码的响应,因为您没有设置响应的内容长度。如果这对您来说没问题,您可以使用以下示例。否则我会尝试看看是否有其他更好的方法来实现这一点。

    注意:这是一个基于 PushStreamContent 和 this 在 MSDN 上关于从 sql server 检索二进制数据的文章的快速示例。

    [RoutePrefix("api/values")]
    public class ValuesController : ApiController
    {
        private const string connectionString = @"your connection string";
    
        [Route("{id}")]
        public HttpResponseMessage GetImage(int id)
        {
            HttpResponseMessage resp = new HttpResponseMessage();
    
            resp.Content = new PushStreamContent(async (responseStream, content, context) =>
            {
                await CopyBinaryValueToResponseStream(responseStream, id);
            });
    
            return resp;
        }
    
        // Application retrieving a large BLOB from SQL Server in .NET 4.5 using the new asynchronous capability
        private static async Task CopyBinaryValueToResponseStream(Stream responseStream, int imageId)
        {
            // PushStreamContent requires the responseStream to be closed
            // for signaling it that you have finished writing the response.
            using (responseStream)
            {
                using (SqlConnection connection = new SqlConnection(connectionString))
                {
                    await connection.OpenAsync();
    
                    using (SqlCommand command = new SqlCommand("SELECT [bindata] FROM [Streams] WHERE [id]=@id", connection))
                    {
                        command.Parameters.AddWithValue("id", imageId);
    
                        // The reader needs to be executed with the SequentialAccess behavior to enable network streaming
                        // Otherwise ReadAsync will buffer the entire BLOB into memory which can cause scalability issues or even OutOfMemoryExceptions
                        using (SqlDataReader reader = await command.ExecuteReaderAsync(CommandBehavior.SequentialAccess))
                        {
                            if (await reader.ReadAsync())
                            {
                                if (!(await reader.IsDBNullAsync(0)))
                                {
                                    using (Stream data = reader.GetStream(0))
                                    {
                                        // Asynchronously copy the stream from the server to the response stream
                                        await data.CopyToAsync(responseStream);
                                    }
                                }
                            }
                        }
                    }
                }
            }// close response stream
        }
    }
    

    【讨论】:

    • 我正在尝试类似的方法,但出现此错误:异步模块或处理程序已完成,而异步操作仍处于挂起状态
    • 您使用的是哪个版本的 Web API?具体你使用的是哪个版本的Microsoft.AspNet.WebApi.Core nuget 包?
    • 您的意思是我的操作没有返回任务?...不,因为我在 PushStreamContent 的 async Func 中执行基于异步的东西...我对上述内容进行了一些修改代码并立即尝试它并且工作正常...您能否分享您的实现,想知道是否有任何细微的差异导致您看到的问题?
    • 我只是无法使上述工作。我三次检查代码无济于事。尝试了其他答案的建议,只要我设置内容长度标题就可以工作
    【解决方案2】:

    您可以编写一个包装流,该流从底层流中读取直到耗尽,然后释放所有相关资源(如 SqlConnection)。

    我不是 WebAPI 专家,所以可能有更优雅的方式来做这件事。但这在任何情况下都适用于流的标准 WebAPI 支持。

    【讨论】:

    • 将此标记为答案,因为我无法让推送流内容正常工作。我创建了一个包装流并设置了内容长度以使其正常工作。如果没有内容长度,它将无法工作(在 IIS 和 selfhost 中使用 owin)
    猜你喜欢
    • 2014-06-29
    • 2013-07-31
    • 2012-07-12
    • 2022-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-06
    相关资源
    最近更新 更多