如何从SQL Server读取大文件?

时间:2018-08-30 07:34:49

标签: c# sql-server out-of-memory

我试图从SQL Server读取文件(650兆字节):

using (var reader = command.ExecuteReader(CommandBehavior.SequentialAccess))
{
   if (reader.Read())
   {
       using (var dbStream = reader.GetStream(0))
       {
          if (!reader.IsDBNull(0))
          {
              stream.Position = 0;
              dbStream.CopyTo(stream, 256);
           }

           dbStream.Close();
         }
       }

       reader.Close();
    }

但是我在OutOfMemoryException上获得了CopyTo()

对于小文件,此代码段可以正常工作。如何处理大文件?

2 个答案:

答案 0 :(得分:1)

您可以一小部分地将数据读写到某些临时文件中。您可以在MSDN - Retrieving Binary Data上看到示例。

// Writes the BLOB to a file (*.bmp).  
FileStream stream;                            
// Streams the BLOB to the FileStream object.  
BinaryWriter writer;                          

// Size of the BLOB buffer.  
int bufferSize = 100;                     
// The BLOB byte[] buffer to be filled by GetBytes.  
byte[] outByte = new byte[bufferSize];    
// The bytes returned from GetBytes.  
long retval;                              
// The starting position in the BLOB output.  
long startIndex = 0;                      

// Open the connection and read data into the DataReader.  
connection.Open();  
SqlDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess);  

while (reader.Read())  
{  

  // Create a file to hold the output.  
  stream = new FileStream(  
    "some-physical-file-name-to-dump-data.bmp", FileMode.OpenOrCreate, FileAccess.Write);  
  writer = new BinaryWriter(stream);  

  // Reset the starting byte for the new BLOB.  
  startIndex = 0;  

  // Read bytes into outByte[] and retain the number of bytes returned.  
  retval = reader.GetBytes(1, startIndex, outByte, 0, bufferSize);  

  // Continue while there are bytes beyond the size of the buffer.  
  while (retval == bufferSize)  
  {  
    writer.Write(outByte);  
    writer.Flush();  

    // Reposition start index to end of last buffer and fill buffer.  
    startIndex += bufferSize;  
    retval = reader.GetBytes(1, startIndex, outByte, 0, bufferSize);  
  }  

  // Write the remaining buffer.  
  writer.Write(outByte, 0, (int)retval);  
  writer.Flush();  

  // Close the output file.  
  writer.Close();  
  stream.Close();  
}  

// Close the reader and the connection.  
reader.Close();  
connection.Close();

确保您将SqlDataReaderCommandBehavior.SequentialAccess一起使用,请注意上面的代码段中的这一行。

 SqlDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess);  

有关CommandBehavior枚举的更多信息,请参见here

编辑:

让我澄清一下。我同意@MickyD,问题的原因不是您是否使用CommandBehavior.SequentialAccess,而是一次读取大文件。

我强调这一点是因为开发人员通常会错过它,他们倾向于按块读取文件,但是如果不设置CommandBehavior.SequentialAccess,他们将会遇到其他问题。尽管它已经发布了原始问题,但在我的回答中着重指出了任何新手。

答案 1 :(得分:0)

  

@MatthewWatson是的var stream = new MemoreStream();有什么不对的地方吗? – Kliver Max 15小时前

您的问题不在于您是否在使用:

`command.ExecuteReader(CommandBehavior.SequentialAccess)` 

...您就是我们所看到的;或者您的流复制缓冲区大小太大(实际上很小),而是您使用的是MemoryStream,如上面的注释中所述。您很有可能正在加载650MB文件两次一次从SQL 加载以及另一个要存储在 MemoryStream 中的文件,从而导致您的 OutOfMemoryException

尽管解决方案是改为写入文件流,但问题的原因并未在接受的答案中突出显示。除非您知道问题的原因,否则您将来将不会学会避免此类问题。