如何在不缓冲的情况下将文件从数据库异步传输到webclient

时间:2013-11-26 15:04:02

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

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

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

那里有样品吗?

2 个答案:

答案 0 :(得分:6)

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

注意:这是一个基于PushStreamContent和MSDN上 this 关于从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
    }
}

答案 1 :(得分:1)

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

我不是WebAPI专家,所以可能有更优雅的方式来做。但是,无论如何,这都适用于流的标准WebAPI支持。