【问题标题】:SQL Server stream file output in ASP.NET MVC and disposing connectionASP.NET MVC 中的 SQL Server 流文件输出和处理连接
【发布时间】:2017-08-14 12:56:04
【问题描述】:

我正在尝试将存储在 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;
                }
            }
        }
    }
}

【问题讨论】:

  • 这不是与其他人的重复,因为我不是在问如何处理流,而是在问如何处理相关的连接对象。我的问题更多是关于确保连接对象被释放的正确方法
  • @musefan 不幸的是,您的新示例 (stackoverflow.com/questions/37956254/…) 没有解决流式传输的需求,并且接受的答案在发送响应之前将整个内容加载到内存流中。
  • 您没有任何可靠的选择。连接是有限的/宝贵的,您需要尽快处理它们。读取所有数据然后关闭连接并发送数据是迄今为止最好的方法。还有其他方法,例如注册和处理对象,但您只是让连接保持打开状态,这样做没有优势。
  • Erik,看起来 HttpContext.Response.RegisterForDispose() 有效,所以我可以流式传输结果,而不必担心完成后连接仍然存在

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


【解决方案1】:

大卫布朗的回答给了我信息。我需要使用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;
}

如果其他人有同样的问题,我添加了这个答案以清楚起见

【讨论】:

  • 你应该接受你自己的答案而不是另一个,这个要好得多,另一个不太努力,可能应该只是一个评论
【解决方案2】:
猜你喜欢
  • 1970-01-01
  • 2015-11-19
  • 2018-09-12
  • 2012-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-17
相关资源
最近更新 更多