在数据库应用项目开发中,经常会使用一些二进制的图像数据,存储和读取显示图像数据主要采用的是路径链接法和内存流法。路径链接法是将图像文件保存在固定的路径下,数据库中只存储图像文件的路径和名称,此方法数据库容量小,存取速度快,但安全性较差;内存流法是将二进制数据直接存储在数据库中,此方法对数据的共享非常方便,安全性相对较高,常用于图像容量不是很大的时候。
本文主要讨论通过SQL Server 2008使用内存流法如何实现二进制图像数据的存储。
1 VARBINARY(MAX)数据类型简介
在SQL Server 2000和更早的版本中,如果每条记录的数据量远远超过了一个单独记录的8K,我们常用IMAGE数据类型以二进制存储该数据,在使用IMAGE数据类型时,数据是不和普通数据存储在一起的。一个被称作指针的很小的二进制值,和普通数据存储在一起,这个二进制值指向数据文件里的数据实际存储的位置。对IMAGE数据进行读取、插入数据时需要使用READTEXT[1]及WRITETEXT命令,这两个命令需使用TEXTPTR函数来获得正确的二进制指针,这个二进制指针用于定位物理文件中的数据,使用起来较麻烦。
虽然在SQL Server 2008中依然提供IMAGE数据类型,但微软计划在未来的SQL Server版本中删除IMAGE数据类型, 用VARBINARY(MAX)数据类型来代替,在该版本下不应当使用IMAGE这种类型。
VARBINARY(MAX) 为可变长度二进制数据,不限最大长度,常用于数据超出 8,000 字节时,可以直接使用insert命令添加数据,使用起来较简单。
2 使用T-SQL语句将图像文件直接读入VARBINARY(MAX)字段
首先在SQL Server 2008的查询窗口中创建测试数据库ImageDB,并建立ImageTable表来存储图像数据:
CREATE DATABASE ImageTest
GO
USE ImageTest;
GO
CREATE TABLE ImageTable
(
ImageData varBinary(MAX)
);
然后使用Insert命令将其插入到ImageTest表中(此处图像文件为),OPENROWSET函数包含访问OLE DB数据源中的远程数据所需的全部连接信息,内置的 BULK 访问接口支持大容量操作,实现的代码为:
INSERT INTO ImageTable (ImageData)
SELECT *
GO
运行Select * from ImageTable语句将会看到该图像的二进制编码,但SQL Server 2008不提供直接查看图片的方法,为了查看图片,我们需要创建应用程序,本文第4部分介绍其实现的方法。
3 在SQL Server中将二进制数据复制到图像文件
在SQL Server 2008中将二进制数据复制到图像文件,要对Windows的文件进行操作,我们需要用到xp_cmdshell扩展存储过程,在使用之前我们需获得执行该命令的权限:
EXEC sp_configure 'show advanced options', 1
GO
RECONFIGURE
GO
EXEC sp_configure 'xp_cmdshell', 1
GO
RECONFIGURE
GO
通过执行xp_cmdshell将二进制数据复制到图像文件:
Go
可以看到C盘下增加了一个图像文件。
4 使用C#.NET存取SQL Server 2008中的图像数据
为在2中创建的ImageTable表添加一个ImagePath字段,用于记录图像的路径及文件名。
在窗体上添加一个按钮,用于实现将图像文件读入SQL Server,实现的基本过程是先通过openFileDialog控件选择要读入的图像文件,将图像文件转换成二进制流,连接数据库后使用insert命令将二进制流数据存储到SQL Server,实现的代码如下:
this.openFileDialog1.ShowDialog();
string file = this.openFileDialog1.FileName;
try
{
SqlConnection sqlConnection = new SqlConnection(@"Data Source=.\\weisql;Initial Catalog=ImageTest;Integrated Security=True");
sqlConnection.Open();
FileStream byteStream = new FileStream(file, FileMode.Open, FileAccess.Read);
byte[] byteImage = new byte[byteStream .Length]; //图像文件转换成二进制流
byteStream .Read(byteImage, 0, (int)byteStream .Length);
string comText = "Insert into ImageTable(ImagePath, ImageData)Values(@ImagePath, @ImageData)";
SqlCommand sqlCommand = new SqlCommand(comText, sqlConnection);
sqlCommand.Parameters.Add("@ImagePath", SqlDbType.Text);
sqlCommand.Parameters.Add("@ImageData", SqlDbType.Binary); sqlCommand.Parameters["@ImagePath"].Value = file;
sqlCommand.Parameters["@ImageData"].Value = byteImage;
sqlCommand.ExecuteNonQuery();
System.Windows.Forms.Application.DoEvents();
sqlConnection.Close();
}
catch (SqlException ex)
{
MessageBox.Show(ex.ToString());
}
MessageBox.Show("已将文件添加到SQL Server,点击“显示图片”查看");
再添加一个按钮,用于实现将SQL Server中二进制数据显示为图像,连接数据库后将ImageTable表的记录绑定到dataGridView控件,虽然dataGridView控件可以将二进制数据显示为图片,但行列间距太小,不好看,所以同时还将图像显示在pictureBox控件中,实现的代码如下:
try
{
SqlConnection sqlConnection = new SqlConnection(@"Data Source=.\\weisql;Initial Catalog=ImageTest;Integrated Security=True");
sqlConnection.Open();
string comText = "Select ImagePath, ImageData from ImageTable";
SqlDataAdapter adapter = new SqlDataAdapter(comText, sqlConnection);
DataTable dt = new DataTable();
adapter.Fill(dt);
this.dataGridView1.DataSource = dt;
SqlCommand sqlCommand = new SqlCommand(comText, sqlConnection);
SqlDataReader dr = sqlCommand.ExecuteReader();
dr.Read();
MemoryStream ms = new MemoryStream((byte[])dr[1] );
Image img = Image.FromStream(ms);
pictureBox1.Image = img;
sqlConnection.Close();
}
catch (SqlException ex)
{
MessageBox.Show(ex.ToString());
}
程序结果如图1所示。
图1 程序运行结果
5 结束语
以上实例在Windows XP操作系统平台上, 采用面向对象的编程环境C#.NET作为前台开发工具、SQL Server2008作为后台数据库服务器调试通过,对有类似需求的开发项目有一定的参考价值。
使用image类型保存图片数据
文章描述的是SQL Server存储图像数据,我们大家都知道SQL Server数据库中的图像数据的存储机制在MIS SQL Server 数据库中,对于小于8000个字节的图像数据,其可以用二进制型(binary、varbinary)来表示。但通常要保存的一些医学影像图片都会大于 8000个字节。
SQL Server提供了一种机制,能存储每行大到 2G的二进制对象(BLOB),这类对象可包括image、text和ntext三种数据类型。Image数据类型存储的是二进制数据,最大长度是 231-1 (2,147,483,7)个字节[2][3]。
BLOB数据在MIS SQL Server存储图像数据系统中的存储方式不同于普通的数据类型,对于普通类型的数据系统直接在用户定义的字段上存储数据值,而对于BLOB类型数据,系统开辟新的存储页面来存放这些数据,表中BLOB类型数据字段存放的仅是一个16个字节的指针,该指针指向存放该条记录的BLOB数据的页面。
2、 SQL Server中图像数据的存取 在MIS SQL Server存储图像数据中,当数据小于 8000 个字节时,可以用普通的SQL操纵语句(SELECT、INSERT、UPDATE、DELETE)来完成对字段的操纵,当数据大于8000个字节时,SQL提供了 WRITETEXT 、READTEXT和UPDATETEXT这三个函数来读取和修改数据。
这三个函数的使用方法为: (1) WRITETEXT {table.column text_ptr}[WITH LOG] table.column为表中的字段,text_ptr为一个16个字节的指针,data为要写的数据值。可 选参数WITH LOG表示是否要写入日志文件中。
例:
| 以下是代码片段: DECLARE @ptrval binary(16) --指针 SELECT @ptrval = TEXTPTR(img_ct) FROM zy_ct WHERE id_ct = 20010101001 WRITETEXT zy_ct.img_ct @ptrval 0x024324142342134214213421421454353452341 (2) READTEXT {table.column text_ptr offset size} [HOLDLOCK] table.column |
例:
| 以下是代码片段: DECLARE @ptrval varbinary(16) SELECT @ptrval = TEXTPTR(img_ct) FROM zy_ct WHERE id_ct = 20010101001 READTEXT zy_ct.img_ct @ptrval 1 25 (3) UPDATETEXT {table_name.dest_column_name dest_text_ptr} { NULL | delete_length}[WITH LOG][ inserted_data| {table_name.src_column_name src_text_ptr}] table_name.dest_column_name |
dest_text_ptr为指向其的指针;insert_offset为偏移量,对于text和image为从 第几开始字节开始写,对于ntext为从第几个字符(双字节)开始写;delete_length 为从insert_offset开始删除delete_length长度的字节(符),为0时不删除,为NULL时 为删除从insert_offset开始到结束的所有数据。要插入的数据为 inserted_data为,也可 是表table_name的src_column_name字段中指针 src_text_ptr所指数据。
例: DECLARE @ptrval binary(16) SELECT @ptrval = TEXTPTR(img_ct) FROM zy_ct WHERE id_ct = 20010101001 UPDATETEXT zy_ct.img_ct @ptrval 16 0x54345 可以看出,这三个函数的使用比较复杂,虽然可以通过生成存贮过程来调用执行SQL Server存储图像数据,但有一个缺陷是在读取数据时,READTEXT函数读取的数据无法直接传递回前端应用程序。
3、 VB 6.0中图像数据的存取 VB 6.0 的ADO Field 对象提供了GetChunk 方法和AppendChunk 方法来存取BLOB数据[1],这两个函数实质是通过API调用WRITETEXT 、READTEXT和UPDATETEXT这三个函数,简化了调用的方法。
(1)GetChunk 和AppendChunk方法介绍 GetChunk 方法检索其部分或全二进制或字符数据[4]。GetChunk 调用返回的数据将赋给“变量”。如果 Size 大于剩余的数据,则 GetChunk 仅返回剩余的数据而无需用空白填充“变量”。如果字段为空,则 GetChunk 方法返回 Null。
每个后续的 GetChunk 调用将检索从前一次 GetChunk 调用停止处开始的数据。但是,如果从一个字段检索数据然后在当前记录中设置或读取另一个字段的值,ADO 将认为已从第一个字段中检索出数据。
如果在第一个字段上再次调用 GetChunk 方法,ADO 将把调用解释为新的 GetChunk 操作并从记录的起始处开始读取。Field 对象的第一个 AppendChunk 调用将数据写入字段,覆盖所有现有的数据,随后的 AppendChunk 调用则添加到现有数据。
由于系统资源总是有限的,如果一次读(存)取大量数据SQL Server存储图像数据,可能会引起服务器、客户机死机或是服务器的性能大大下降,因此使用这两个函数时,要将图像数据进行分段读写。下载本文