版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。
需要北风数据库的请留言自己的信箱。
北风数据库中,类别表的图片字段在【数据表视图】中显示为Bitmap Image:
图19-30 图片字段的数据显示为Bitmap Image
它是一个OLE对象,实际就是一个字节数组:
图19-30 图片字段在设计视图中为OLE对象
双击某一个类别的Bitmap Image 可以打开画图查看图片:
图19-32 双击默认会使用画图打开
对于Access数据库中保存的二进制数据,应该使用OleDbCommand的ExecuteReader方法加参数SequentialAccess来打开获得对应的OleDbDataReader对象。
SequentialAccess不会加载整行,而是使 OleDbDataReader将数据作为流来加载。然后可以使用GetBytes或GetChars方法来指定开始读取操作的字节位置以及正在返回的数据的有限的缓冲区大小。尽管无需读取每个字段,但是需要按照字段的返回顺序读取它们。 一旦已经读过返回的数据流中某个位置的内容,就不能再从 OleDbDataReader中读取该位置或该位置之前的数据。
【例 19.13】【项目:code19-013】读取数据表中的图片。
根据BMP文件的格式可知,BMP前两个字节是BMP文件头,为"BM",对应的十六进制值为 42 4D,由于北风数据库中的数据表使用的是OLE对象方式保存图片,所以它在BMP数据基础上增加了其它的数据。在获得图片字段数据后,只需要查找42 4D开始的位置,获得开始位置之后的数据就可以还原这个BMP图像了。
具体的代码如下:
……代码略
private void Form1_Load(object sender, EventArgs e)
{
//建立OleDbConnection对象实例
conn = new OleDbConnection();
//设置OleDbConnection的连接字符串
conn.ConnectionString = "Provider = Microsoft.Jet.OLEDB.4.0;data source=C:\\lessons\\Northwind.mdb;";
//打开数据连接
conn.Open();
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
OleDbCommand command = new OleDbCommand();
command.CommandText = "select 类别名称 from 类别";
command.Connection = conn;
//从类别表中获得类别名称并增加到ComboBox1中
OleDbDataReader reader = command.ExecuteReader();
if (reader.HasRows)
while (reader.Read())
comboBox1.Items.Add(reader.GetString(0));
reader.Close();
comboBox1.DropDownStyle = ComboBoxStyle.DropDownList;
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
OleDbCommand command = new OleDbCommand();
command.CommandText = "select 图片 from 类别 where 类别名称='" + comboBox1.Text + "'";
command.Connection = conn;
//使用OleDbDataReader读取图片,使用SequentialAccess模式
OleDbDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess & CommandBehavior.SingleResult & CommandBehavior.SingleRow);
try
{
//存放从字段获得的数据
byte[] fieldsData = new byte[] { };
//每次从字段读取的字节数组
byte[] buffer;
//每次从字段读取的字节数组的长度
int bufferLength = 1024;
buffer = new byte[bufferLength];
//每次使用OleDbDataReader的GetBytes方法时返回实际读取字节数的长度
long readLength;
int pos = 0;
//开始读取
reader.Read();
//读取数据,数据较小时可以使用
fieldsData =(byte[]) reader[0];
或者将上一句代码替换为
循环读取数据,数据较大的时候使用
//while (true)
//{
// //使用GetBytes方法读取数据
// readLength = reader.GetBytes(0, pos * bufferLength, buffer, 0, bufferLength);
// int imgDataLength;
// imgDataLength = fieldsData.Length;
// //保留fieldsData原来的数据基础上扩大fieldsData的大小
// Array.Resize(ref fieldsData,imgDataLength + (int)readLength);
// //将新读取的数据拷贝到fieldsData
// Array.Copy(buffer, 0, fieldsData, imgDataLength, readLength);
// //如果实际读取数据的长度小于设定的长度,那么表示已经读取完毕,退出循环
// if (readLength < bufferLength)
// break;
// pos += 1;
//}
//判断是否包含Bmp的标志 BM,即对应的十六进制值为 42 4D
int bmpSign = -1;
for(int i = 0; i< fieldsData.Length - 1;i++)
{
if( fieldsData[i] == 0x42 & fieldsData[i + 1] == 0x4D)
{
bmpSign = i;
break;
}
}
if(bmpSign == -1)
{
MessageBox.Show("不是有效的图片文件");
return;
}
//从fieldsData中提取出图像的数据保存到imgData
byte[] imgData = new byte[fieldsData.Length - bmpSign];
Array.Copy(fieldsData, bmpSign, imgData, 0, imgData.Length);
//使用字节数组初始化一个MemoryStream对象
System.IO.MemoryStream ms = new System.IO.MemoryStream(imgData);
//从流获得一个image对象
Image img = Image.FromStream(ms);
pictureBox1.Image = img;
//关闭流
ms.Close();
}
catch( Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
//关闭OleDbDataReader
reader.Close();
}
}
运行结果如下图所示:
图19-33 显示类别图片
注意:以上操作是比较常见读取二进制数据值的一种操作,还可以使用以下代码:
fieldsData =(byte[]) reader[0];
替换while { }部分的代码。系统将获得指定字段里面的数据并自动转换为相应的数据类型的值。以上代码更简洁,可以在知道字段对应数据较少的情况下使用以上代码。
【例 19.14】【项目:code19-014】向数据表中写入图片。
private void btnChooseImage_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "所有图片|*.png;*.jpg;*.gif";
if (ofd.ShowDialog() != DialogResult.OK)
return;
picType.Image = Image.FromFile(ofd.FileName);
}
private void btnAdd_Click(object sender, EventArgs e)
{
//检查数据是否符合要求
if (txtType.Text.Trim() == "")
return;
if (txtTypeInfo.Text.Trim() == "")
return;
if (picType.Image == null)
return;
OleDbCommand odcommand = new OleDbCommand();
//更新数据表的SQL语句
odcommand.CommandText = "insert into 类别(类别名称,说明,图片) values(@typename,@typeinfo,@imgType)";
odcommand.Connection = conn;
//添加相应的数据参数
odcommand.Parameters.Add("@typename", OleDbType.VarChar);
odcommand.Parameters["@typename"].Value = txtType.Text;
odcommand.Parameters.Add("@typeinfo", OleDbType.VarChar);
odcommand.Parameters["@typeinfo"].Value = txtTypeInfo.Text;
//按照数据表中类别的图片大小172*120建立图片
Bitmap bmp = new Bitmap(172, 120);
Graphics g = Graphics.FromImage(bmp);
g.DrawImage(picType.Image, new Rectangle(0, 0, 172, 120), new Rectangle(0, 0, picType.Image.Width, picType.Image.Height), GraphicsUnit.Pixel);
g.Dispose();
System.IO.MemoryStream ms = new System.IO.MemoryStream();
//将图片以bmp格式保存到内存流中
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
//从内存流将数据保存到字节数组
byte[] imgBuff = new byte[ms.Length];
ms.Position = 0;
imgBuff = ms.ToArray();
ms.Close();
//添加数据参数,这里使用LongVarBinary类型
odcommand.Parameters.Add("@imgType", OleDbType.LongVarBinary);
//对应的数据为字节数组
odcommand.Parameters["@imgType"].Value = imgBuff;
//执行ExecuteNonQuery
odcommand.ExecuteNonQuery();
}
运行结果如下图所示:
图19-34 向类别表中新增数据
由于这里只是保存的图片本身数据,而不是像类别表中图片字段是一个OLE对象,还添加有附加数据,所以,新增的图片数据不能像原有数据一样可以被画图程序打开。但是,使用【例 19.13】中的代码是可以正确显示图片的。
学习更多vb.net知识,请参看vb.net 教程 目录
学习更多C#知识,请参看C#教程 目录