2011-03-20 87 views
33

我有一个名为Blob(Id(int),Data(Image))的表。我需要使用SqlDataReader来获取图像数据。请注意,我不想Response.Binarywrite()数据到浏览器。我只需要将该二进制数据作为byte []在内部操作中使用。我唯一能想到的方式是使用SqlDataReader获取id,然后再次使用SqlCommand.ExecuteScalar()将byte []作为给定id。我可以只使用SqlDataReader(SqlCommand.ExecuteReader)以byte []形式获取图像数据吗?我错过了什么?使用SqlDataReader获取二进制数据

回答

57

你应该可以通过:(byte[])reader.Items["Data"]

另请注意,image数据类型已弃用,并且将在未来版本的SQL Server中删除;改为使用varbinary(max)

+17

甚至更​​短:'(byte [])reader [“Data”]' – Cosmin 2012-08-30 12:29:02

+2

我甚至没有在.NET 4.0中的'SqlDataReader'上看到'Items'集合。 – Zack 2015-05-22 13:50:43

+0

是否已弃用Items集合? – 2015-06-03 14:33:10

16

是的,您可以使用SqlDataReader.GetBytes。您可能想要在第一次调用中为缓冲区传入null,以找出有多少数据,然后用适当大小的缓冲区再次调用它。

可能只能使用索引器并将结果转换为字节数组 - 我不确定。值得一试:)

+1

IND NDX = rdr.GetOrdinal( “”);如果(rdr.IsDBNull(ndx)) { long size = rdr.GetBytes(ndx,0,null,0,0); //获取数据长度 byte [] values = new byte [size]; int bufferSize = 1024; long bytesRead = 0; int curPos = 0; while(bytesRead 2011-03-20 20:33:29

+0

@ydobonmai:当您已经拥有合适大小的缓冲区时,为什么还要使用不同的缓冲区大小?你应该使用'bytesRead'而不是'curPos';你现在总是假设它读取了一个完整的缓冲区数量。 – 2011-03-21 06:24:30

+0

SqlDataReader.GetBytes()听起来像一个非常有效的方法,因为您可以重用相同的数组(如果它具有适当的大小)。所以你不需要像(byte [])reader [“Data”]那样为每一列创建一个新的数组。 – mmmmmmmm 2015-05-06 19:24:39

7

MSDN。不知道为什么我以前找不到。

SqlConnection pubsConn = new SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=pubs;"); 
    SqlCommand logoCMD = new SqlCommand("SELECT pub_id, logo FROM pub_info", pubsConn); 

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

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

    string pub_id = "";      // The publisher id to use in the file name. 

    // Open the connection and read data into the DataReader. 
    pubsConn.Open(); 
    SqlDataReader myReader = logoCMD.ExecuteReader(CommandBehavior.SequentialAccess); 

    while (myReader.Read()) 
    { 
     // Get the publisher id, which must occur before getting the logo. 
     pub_id = myReader.GetString(0); 

     // Create a file to hold the output. 
     fs = new FileStream("logo" + pub_id + ".bmp", FileMode.OpenOrCreate, FileAccess.Write); 
     bw = new BinaryWriter(fs); 

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

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

     // Continue reading and writing while there are bytes beyond the size of the buffer. 
     while (retval == bufferSize) 
     { 
     bw.Write(outbyte); 
     bw.Flush(); 

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

     // Write the remaining buffer. 
     if(retval > 0) // if file size can divide to buffer size 
      bw.Write(outbyte, 0, (int)retval); //original MSDN source had retval-1, a bug 
     bw.Flush(); 

     // Close the output file. 
     bw.Close(); 
     fs.Close(); 
    } 

    // Close the reader and the connection. 
    myReader.Close(); 
    pubsConn.Close(); 
+4

有一点需要注意:如果你没有设置CommandBehavior。对ExecuteReader调用进行SequentialAccess(例如从数据层的另一部分获取读取器),它会对您的内存做出糟糕的事情(为GetBytes()的每次调用创建一个Blob大小的字节数组)此解决方案应该只用于CommandBehavior.SequentialAccess,如此处所示。 – Tao 2011-05-27 14:51:33

+1

任何人都可以提供关于如何决定缓冲区大小的意见吗?该示例使用100,但为什么不是10,为什么不是10,000?人们需要考虑哪些因素? – 2013-11-27 07:50:23

1

无需使用读卡器。只需使用数据集从数据库中提取值(使用存储的Proc或任何其他方法),然后只需使用字节(下面的代码)进行类型转换并将其存储在字节数组中即可。你的工作已经完成。

byte[] productImage; 
productImage = (byte[])ds.Tables[0].Rows[0]["Image"]; 
9

在.NET框架4.5,您可以使用GetStream方法来访问二进制数据作为流。

0

使用此功能,用于安全且灵活的字节读:

/// <summary> 
    /// Reads all available bytes from reader 
    /// </summary> 
    /// <param name="reader"></param> 
    /// <param name="ordinal"></param> 
    /// <returns></returns> 
    private byte[] GetBytes(SqliteDataReader reader, int ordinal) 
    { 
     byte[] result = null; 

     if (!reader.IsDBNull(ordinal)) 
     { 
      long size = reader.GetBytes(ordinal, 0, null, 0, 0); //get the length of data 
      result = new byte[size]; 
      int bufferSize = 1024; 
      long bytesRead = 0; 
      int curPos = 0; 
      while (bytesRead < size) 
      { 
       bytesRead += reader.GetBytes(ordinal, curPos, result, curPos, bufferSize); 
       curPos += bufferSize; 
      } 
     } 

     return result; 
    }