使用存储过程和C#在SQL Server中存储和检索图像






4.88/5 (52投票s)
一篇关于如何使用存储过程和 C# 从 SQL Server 2008 存储和检索图像的介绍。
引言
首先,我必须告诉您,我不是专家,但我会尽力解释解决方案。在这篇文章中,我将解释如何使用 C# 作为前端编程语言和存储过程作为 SQL Server 后端语言,从 SQL Server 数据库存储和检索图像。撰写关于此主题文章的原因是给初学者一个正确的理解。
必备组件
您需要对存储过程和 C# 语言有基本的了解。
使用的工具
- SQL Server 2008
- Visual Studio 2010
- C# (Windows 窗体应用程序)
准备开发环境
SQL Server 环境
创建表
在此示例中,我将只使用一个名为 ImageData 的表,它只包含两个字段:ImageID 和 ImageData,这些字段的数据类型为 int
和 image
。使用下面的 SQL 脚本创建表。
CREATE TABLE [dbo].[ImageData]
(
[ImageID] [int] IDENTITY(1,1) NOT NULL,
[ImageData] [image] NULL,
CONSTRAINT [PK_ImageData] PRIMARY KEY CLUSTERED
(
[ImageID] ASC
)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
)
ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
创建存储过程
在此示例中,我将使用四个(4)个存储过程,分别名为 ReadAllImage, ReadAllImageIDs, ReadImage, SaveImage,并使用以下 SQL 脚本创建这些过程。
CREATE proc [dbo].[ReadAllImage] as
SELECT * FROM ImageData
GO
CREATE proc [dbo].[ReadAllImageIDs] as
SELECT ImageID FROM ImageData
GO
CREATE proc [dbo].[ReadImage] @imgId int as
SELECT ImageData FROM ImageData
WHERE ImageID=@imgId
GO
CREATE proc [dbo].[SaveImage] @img image as
INSERT INTO ImageData(ImageData)
VALUES (@img)
GO
Visual Studio 环境
创建 Windows 窗体
在此示例中,我将只使用一个窗体,并根据下表设置基本属性。
控件名称 | 属性名称 | 属性值 |
表单 |
名称 | UsingSPs |
文本 | 使用 C#.NET 从 SQL Server 存储和检索图像 | |
Button1 | 名称 | btnLoadAndSave |
文本 | <<--加载和保存图像-->> | |
Button2 | 名称 | btnRefresh |
文本 | 刷新 | |
Button3 | 名称 | btnDisplayImage |
文本 | 显示图像 | |
ComboBox | 名称 | cmbImageID |
GroupBox | 名称 | grbPicBox |
文本 | 图像显示 | |
Anchor | 顶部、底部、左侧、右侧 | |
PictureBox | 名称 | picImage |
Dock | Fill |
开始编码
现在我们已经具备了开始编码的所有条件,这是我们需要高度关注开发的部分。无论如何,我将尽力解释编码,那么,让我们开始我们的旅程吧。
在此示例中,除了窗体之外,我还会使用一个名为 DBHandler
的类,该类的目的是处理数据库连接详细信息。这是该类的代码。
处理数据库连接字符串
public class DBHandler
{
public static string SrvName = @"DBSERVER"; //Your SQL Server Name
public static string DbName = @"DB";//Your Database Name
public static string UsrName = "us";//Your SQL Server User Name
public static string Pasword = "xxxx";//Your SQL Server Password
/// <summary>
/// Public static method to access connection string throw out the project
/// </summary>
/// <returns>return database connection string</returns>
public static string GetConnectionString()
{
return "Data Source=" + SrvName + "; initial catalog=" + DbName + "; user id="
+ UsrName + "; password=" + Pasword + ";";//Build Connection String and Return
}
}
选择图像并存储到数据库
在开始编码之前,请在代码中添加以下命名空间。
using System.IO;
using System.Data;
using System.Data.SqlClient;
在这里,我将逐步解释 btnLoadAndSave 按钮的单击事件过程。
- 创建与数据库的连接。
- 创建类型为 OpenFileDialog 的对象
fop
。 - 设置对象
fop
的 InitialDirectory 属性。 - 设置对象
fop
的 Filter 属性。(在这里用户只能选择 .jpg 文件) - 向用户显示打开文件对话框,只有当用户选择一个图像时,才会进入 if 块。
- 创建一个文件流对象
FS
,并将其与用户选择的文件关联。 - 创建一个大小等于用户选择的文件流长度的字节数组。
- 将用户选择的文件流读取到字节数组中。
- 检查与数据库的连接是否已关闭。
- 如果连接已关闭,则仅打开连接。
- 通过传递存储过程名称和数据库连接来创建一个 SQL 命令对象
cmd
。 - 将
cmd
对象的 CommandType 属性设置为存储过程类型。 - 向
cmd
对象添加参数并为其设置值。 - 通过调用对象
cmd
的 ExecuteNonQuery() 方法来执行 SQL 命令。 - 调用用户定义的方法将图像 ID 加载到组合框。(此方法稍后会解释,所以现在不用担心)
- 向用户显示保存成功的消息。
- 捕获上述代码执行过程中发生的任何错误。
- 最后,检查数据库连接是否已打开,如果已打开,则仅关闭连接。
下面演示了完整的选择和存储图像到数据库的代码。
SqlConnection con = new SqlConnection(DBHandler.GetConnectionString());
try
{
OpenFileDialog fop = new OpenFileDialog();
fop.InitialDirectory = @"C:\";
fop.Filter = "[JPG,JPEG]|*.jpg";
if (fop.ShowDialog() == DialogResult.OK)
{
FileStream FS = new FileStream(@fop.FileName, FileMode.Open, FileAccess.Read);
byte[] img = new byte[FS.Length];
FS.Read(img, 0, Convert.ToInt32(FS.Length));
if (con.State == ConnectionState.Closed)
con.Open();
SqlCommand cmd = new SqlCommand("SaveImage", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@img", SqlDbType.Image).Value = img;
cmd.ExecuteNonQuery();
loadImageIDs();
MessageBox.Show("Image Save Successfully!!", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
MessageBox.Show("Please Select a Image to save!!", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
if (con.State == ConnectionState.Open)
con.Close();
}
检索和显示图像
在这里,我将逐步解释 btnDisplayImage 按钮的单击事件过程。
- 检查用户是否从组合框中选择了图像 ID。
- 检查图片框是否包含图像。
- 如果图片框中有图像,则清除该图像。
- 创建与数据库的连接。
- 通过传递存储过程名称和数据库连接来创建一个 SQL 命令对象
cmd
。 - 将
cmd
对象的 CommandType 属性设置为存储过程类型。 - 向
cmd
对象添加参数并为其设置值。 - 通过传递先前创建的
cmd
对象来创建一个 SQL 数据适配器对象adp
。 - 创建一个数据表对象
dt
来保存cmd
对象的结果。 - 检查与数据库的连接是否已关闭。
- 如果连接已关闭,则仅打开连接。
- 通过调用
adp
对象的 fill 方法,使用数据填充对象dt
。 - 检查对象
dt
是否包含任何数据行。 - 通过传递图像的字节数组来创建一个内存流对象
ms
。 - 通过从内存流创建图像来设置图片框的 Image 属性。
- 将图片框的 SizeMode 属性设置为 Stretch。
- 调用图片框的 Refresh 方法。
- 捕获上述代码执行过程中发生的任何错误。
- 最后,检查数据库连接是否已打开,如果已打开,则仅关闭连接。
下面演示了完整的检索和显示图像的代码。
if (cmbImageID.SelectedValue != null)
{
if (picImage.Image != null)
picImage.Image.Dispose();
SqlConnection con = new SqlConnection(DBHandler.GetConnectionString());
SqlCommand cmd = new SqlCommand("ReadImage", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@imgId", SqlDbType.Int).Value =
Convert.ToInt32(cmbImageID.SelectedValue.ToString());
SqlDataAdapter adp = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
try
{
if (con.State == ConnectionState.Closed)
con.Open();
adp.Fill(dt);
if (dt.Rows.Count > 0)
{
MemoryStream ms = new MemoryStream((byte[])dt.Rows[0]["ImageData"]);
picImage.Image = Image.FromStream(ms);
picImage.SizeMode = PictureBoxSizeMode.StretchImage;
picImage.Refresh();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
if (con.State == ConnectionState.Open)
con.Close();
}
}
else
{
MessageBox.Show("Please Select a Image ID to Display!!",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
将所有图像 ID 加载到组合框
在此示例中,我使用了一个名为 loadImageIDs() 的通用方法,该方法的目的是将所有图像 ID 加载到组合框。将此过程写成单独的方法是因为我将在应用程序的多个地方使用它。提及这些地方。
- 在
btnRefresh
按钮的 Click 事件中。 - 在
btnLoadAndSave
按钮的 Click 事件中。 - 在
UsingSPs
窗体的 Load 事件中。
在这里,我将逐步解释 loadImageIDs() 用户定义方法的进程。
- 创建与数据库的连接。
- 通过传递存储过程名称和数据库连接来创建一个 SQL 命令对象
cmd
。 - 将
cmd
对象的 CommandType 属性设置为存储过程类型。 - 通过传递先前创建的
cmd
对象来创建一个 SQL 数据适配器对象adp
。 - 创建一个数据表对象
dt
来保存cmd
对象的结果。 - 检查与数据库的连接是否已关闭。
- 如果连接已关闭,则仅打开连接。
- 通过调用
adp
对象的 fill 方法,使用数据填充对象dt
。 - 检查对象
dt
是否包含任何数据行。 - 将
cmbImageID
对象的 DataSource 属性设置为dt
对象。 - 将
cmbImageID
对象的值成员(ValueMember)属性设置为数据库表中的 ImageID 列。 - 将
cmbImageID
对象的显示成员(DisplayMember)属性设置为数据库表中的 ImageID 列。 - 将
cmbImageID
对象的 SelectedIndex 属性设置为值 0。 - 捕获上述代码执行过程中发生的任何错误。
- 最后,检查数据库连接是否已打开,如果已打开,则仅关闭连接。
下面演示了将所有图像 ID 加载到组合框的完整代码。
private void loadImageIDs()
{
#region Load Image Ids
SqlConnection con = new SqlConnection(DBHandler.GetConnectionString());
SqlCommand cmd = new SqlCommand("ReadAllImageIDs", con);
cmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter adp = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
try
{
if (con.State == ConnectionState.Closed)
con.Open();
adp.Fill(dt);
if (dt.Rows.Count > 0)
{
cmbImageID.DataSource = dt;
cmbImageID.ValueMember = "ImageID";
cmbImageID.DisplayMember = "ImageID";
cmbImageID.SelectedIndex = 0;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
if (con.State == ConnectionState.Open)
con.Close();
}
#endregion
}
关注点
我希望这篇文章能帮助您理解和学习如何使用 C#.net 从 SQL Server 存储和检索图像。
我试图以简单的方式逐步解释整个过程。希望它能帮助您理解这篇文章。
如果您对本文有任何意见,请随时使用下方的评论和讨论区,因为您宝贵的想法将有助于我改进本文或撰写新文章。
历史
- 2012 年 3 月 27 日:初稿