高效的读取二进制数据

对于DataReader读取大的二进制数据时,一次性把数据加载到内存中并不是什么好的做法。有更好的做法是在执行DataReader.ExecuteReader()时,使用CommandBehavior.SequentialAccess 枚举作为参数。这样就可以通过DataReader.GetBytes()来逐块访问数据。

先来介绍一下CommandBehavior枚举:

提供对查询结果和查询对数据库的影响的说明。

 成员名称说明
由 .NET Compact Framework 支持 CloseConnection 在执行该命令时,如果关闭关联的 DataReader 对象,则关联的 Connection 对象也将关闭。 
由 .NET Compact Framework 支持 Default 此查询可能返回多个结果集。执行查询可能会影响数据库状态。Default 不设置 CommandBehavior 标志,因此调用 ExecuteReader(CommandBehavior.Default)在功能上等效于调用 ExecuteReader()。 
由 .NET Compact Framework 支持 KeyInfo 此查询返回列和主键信息。  

当 KeyInfo 用于命令执行时,提供程序将为现有主键列和时间戳列的结果集附加额外的列。

当使用 KeyInfo 时,用于 SQL Server 的 .NET Framework 数据提供程序将在要执行的语句前加上 SET FMTONLY OFF 和 SET NO_BROWSETABLE ON。用户应该注意潜在的副作用,例如对 SET FMTONLY ON 语句的使用产生的干扰。有关更多信息,请参见“SQL Server 联机丛书”。

由 .NET Compact Framework 支持 SchemaOnly 查询仅返回列信息。当使用 SchemaOnly 时,用于 SQL Server 的 .NET Framework 数据提供程序将在要执行的语句前加上 SET FMTONLY ON。 
由 .NET Compact Framework 支持 SequentialAccess 提供一种方法,以便 DataReader 处理包含带有大二进制值的列的行。SequentialAccess 不是加载整行,而是使 DataReader 将数据作为流来加载。然后可以使用GetBytes 或 GetChars 方法来指定开始读取操作的字节位置以及正在返回的数据的有限的缓冲区大小。 

当指定 SequentialAccess 时,尽管无需读取每个列,但是需要按照列的返回顺序读取它们。一旦已经读过返回的数据流中某个位置的内容,就不能再从 DataReader中读取该位置或该位置之前的数据。当使用 OleDbDataReader 时,可重新读取当前列的值,直到读过它。当使用 SqlDataReader 时,一次只能读取一个列值。

由 .NET Compact Framework 支持 SingleResult 查询返回一个结果集。 
由 .NET Compact Framework 支持 SingleRow 查询应返回一行。执行查询可能会影响数据库的状态。一些 .NET Framework 数据提供程序可能(但不要求)使用此信息来优化命令的性能。用 OleDbCommand 对象的 ExecuteReader 方法指定 SingleRow 时,用于 OLE DB 的 .NET Framework 数据提供程序使用 OLE DB IRow 接口(如果可用)执行绑定。否则,它使用 IRowset 接口。如果您的 SQL 语句应该只返回一行,则指定 SingleRow 还可以提高应用程序性能。在执行返回多个结果集的查询时,可以指定 SingleRow。在这种情况下,仍返回多个结果集,但每个结果集只有一行。 

IDbCommand 及从它派生的任何类的 ExecuteReader 方法都使用 CommandBehavior 值。

可以使用这些值的按位组合。

 

1 string connstr = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["sqlstr"].ConnectionString;
2 using (SqlConnection con = new SqlConnection(connstr))
3 {
4
5 SqlCommand cmd = con.CreateCommand();
6 cmd.CommandText = "select * from Categories where categoryid=12";
7 cmd.CommandType = System.Data.CommandType.Text;
8
9 con.Open();
10 //当操作大量带二进制的列时,使用System.Data.CommandBehavior.SequentialAccess,就不是一下加载整行了。
11   SqlDataReader reader = cmd.ExecuteReader(System.Data.CommandBehavior.SequentialAccess);
12 Response.ContentType = "image/jpg";
13 Stopwatch watch = new Stopwatch();
14 watch.Start();
15 if (reader.Read())
16 {
17
18 ////常规操作
19   //byte[] bytes = (byte[])reader[3];
20 //Response.ContentType = "image/jpg";
21 //Response.BinaryWrite(bytes);
22
23 //高效分块读取二进制数据,每次读取10KB
24   int bufferSize = 10240;
25 byte[] bytes = new byte[bufferSize];
26 long bytesRead;
27 long readFrom = 0;
28 //MemoryStream ms = new MemoryStream();
29   do
30 {
31 //简单介绍一下GetBytes()的参数
32 //GetBytes([要读取reader的第几个列,从零开始],[在这个列的而今之中从第几个索引开始读],[要将字节流读入的缓冲区],[缓冲区中写入操作开始位置的索引],[要复制到缓冲区中的最大长度])
33   bytesRead = reader.GetBytes(3, readFrom, bytes, 0, bufferSize);
34
35 //ms.Write(bytes, 0, bufferSize);
36 Response.BinaryWrite(bytes);
37 readFrom += bufferSize;
38
39 } while (bytesRead == bufferSize);
40
41 //ms.Flush();
42 //ms.Close();
43
44 //byte[] b = ms.ToArray();
45 //Response.BinaryWrite(b);
46
47 ////写入文件
48 //string path1 = Server.MapPath("~/t1.jpg");
49 //FileStream fs = new FileStream(path1, FileMode.Create);
50 //fs.Write(bytes, 0, bytes.Length);
51 //fs.Flush();
52 //fs.Close();
53 }
54
55 watch.Stop();
56 long sec = watch.ElapsedMilliseconds;
57 reader.Close();
58 con.Close();
59 }


原文地址:https://www.cnblogs.com/zxhoo/p/1949287.html