ASP.NET MVC中的SQL Server流文件输出和处理连接

时间:2017-08-14 12:56:04

标签: c# .net sql-server asp.net-mvc asp.net-core

我正在尝试将存储在sql server中的内容作为varbinary(max)流式传输到客户端。我能够使它工作,但连接将保持打开,直到垃圾收集。如果在返回结果之前丢弃连接,读取器或蒸汽,则会导致对象处理错误。

我希望避免将数据复制到内存中(因为它可能很大),同时在完成后正确处理对象。什么是实现这两个目标的最佳方式。

如果相关,我使用的是.NET Core 2.0。

更新:这不是其他人(How do I dispose my filestream when implementing a file download in ASP.NET?)的重复,因为我不是在问如何处理流,而是如何处理相关的连接对象。我的问题更多的是确保连接对象被处理的正确方法

下面的代码成功返回结果,但保留了一个非处理连接:

public async Task<IActionResult> DownloadFile(Guid FileId)
{
    var connection = new SqlConnection(DatabaseService.ConnectionString);

    await connection.OpenAsync();
    var command = connection.CreateCommand();

    command.CommandText = "select FileName, FileContent from Files where FileId=@FileId";
    command.CommandType = System.Data.CommandType.Text;
    command.Parameters.AddWithValue("@FileId", FileId);

    var reader = await command.ExecuteReaderAsync(System.Data.CommandBehavior.SequentialAccess | System.Data.CommandBehavior.SingleRow);

    if (!await reader.ReadAsync())
        return NotFound();
    var attachmentName = Convert.ToString(reader[0]);

    var stream = reader.GetStream(1);

    var response = File(stream, "application/octet-stream", attachmentName);
    return response;
}

下面的代码处理了该对象,但由于首先处理了该内容,因此无法对内容进行流式传输

public async Task<IActionResult> DownloadFile(Guid FileId)
{
    using (var connection = new SqlConnection(DatabaseService.ConnectionString))
    {

        await connection.OpenAsync();
        using (var command = connection.CreateCommand())
        {

            command.CommandText = "select FileName, FileContent from Files where FileId=@FileId";
            command.CommandType = System.Data.CommandType.Text;
            command.Parameters.AddWithValue("@FileId", FileId);

            using (var reader = await command.ExecuteReaderAsync(System.Data.CommandBehavior.SequentialAccess | System.Data.CommandBehavior.SingleRow))
            {

                if (!await reader.ReadAsync())
                    return NotFound();
                var attachmentName = Convert.ToString(reader[0]);

                using (var stream = reader.GetStream(1))
                {

                    var response = File(stream, "application/octet-stream", attachmentName);
                    return response;
                }
            }
        }
    }
}

2 个答案:

答案 0 :(得分:2)

大卫布朗的回答给了我相关信息。我需要使用HttpContext.Response.RegisterForDispose();注册一次性组件。这可确保在请求完成后处理它。

以下是更新后的代码

public async Task<IActionResult> DownloadFile(Guid FileId)
{
    var connection = new SqlConnection(DatabaseService.ConnectionString);
    HttpContext.Response.RegisterForDispose(connection);

    await connection.OpenAsync();
    var command = connection.CreateCommand();
    HttpContext.Response.RegisterForDispose(command);

    command.CommandText = "select FileName, FileContent from Files where FileId=@FileId";
    command.CommandType = System.Data.CommandType.Text;
    command.Parameters.AddWithValue("@FileId", FileId);

    var reader = await command.ExecuteReaderAsync(System.Data.CommandBehavior.SequentialAccess | System.Data.CommandBehavior.SingleRow);
    HttpContext.Response.RegisterForDispose(reader);

    if (!await reader.ReadAsync())
        return NotFound();
    var attachmentName = Convert.ToString(reader[0]);

    var stream = reader.GetStream(1);
    HttpContext.Response.RegisterForDispose(stream);

    var response = File(stream, "application/octet-stream", attachmentName);
    return response;
}

如果其他人有同样的问题我添加了这个答案

答案 1 :(得分:1)