2017-08-14 28 views
1

我想将存储在sql server中的内容作为varbinary(max)存储到客户端。我能够使其工作,但连接将保持打开,直到垃圾收集。如果在返回结果之前处理连接,读取器或蒸汽,则会导致对象出错。ASP.NET MVC中的SQL Server流文件输出和处理连接

我正在寻找避免将数据复制到内存中(因为它可能很大),同时也适当处理完成后的对象。达到这两个目标的最好方法是什么?

我正在使用.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 [email protected]"; 
    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 [email protected]"; 
      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; 
       } 
      } 
     } 
    } 
} 
+0

这不是从别人重复,因为我不问如何处置流,但而是如何处置相关的连接对象。我的问题是更多关于确保连接对象被丢弃的正确方法 – webwake

+0

@musefan不幸的是你的新例子(https://stackoverflow.com/questions/37956254/asp-net-core-mvc-get-file-from-database并且呈现为图像)不解决流式传输的需要,并且接受的答案在发送响应之前将整个内容加载到存储器流中。 – webwake

+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 [email protected]"; 
    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; 
} 

我加入这个答案清晰,如果别人有同样的问题

+0

你应该接受你自己的答案,而不是另一个,这是一个好得多,另一个没有多少努力,可能应该只是一个评论 – musefan