Obtaining BLOB Values from a Database
The default behavior of the DataReader is to
load incoming data as a row as soon as an entire row of data is available. Binary large
objects (BLOBs) need to be treated differently, however, because they can contain
gigabytes of data that cannot be contained in a single row. The
Command.ExecuteReader method has an overload which will take a
CommandBehavior argument to modify the default behavior of the DataReader. You can
pass CommandBehavior.SequentialAccess to the ExecuteReader method to modify the
default behavior of the DataReader so that instead of loading rows of data, it will load
data sequentially as it is received. This is ideal for loading BLOBs or other large data
structures. Note that this behavior may differ depending on your data source.
Provides a way for the DataReader to handle rows
that contain columns with large binary values
BLOBs. Rather than loading the entire row, SequentialAccess
enables the DataReader to load data as a stream. When setting the DataReader to
use SequentialAccess, it is important to note the sequence in which you access the
fields returned. The default behavior of the DataReader, which loads an entire row as
soon as it is available, allows you to access the fields returned in any order until the
next row is read. When using SequentialAccess however, you must access the different
fields returned by the DataReader in order. For example, if your query returns three
columns, the third of which is a BLOB, you must return the values of the first and second
fields before accessing the BLOB data in the third field. If you access the third field
before the first or second fields, the first and second field values will no longer be
available. This is because SequentialAccess has modified the DataReader to return data in
sequence and the data will not be available after the DataReader has read past
it.
When accessing the data in the BLOB field, use
the GetBytes or GetChars typed accessors of the DataReader, which fill an
array with data. You can also use GetString for character data, however to conserve
system resources you may not want to load an entire BLOB value into a single string
variable. You can specify a specific buffer size of data to be returned, and a starting
location for the first byte or character to be read from the returned data. GetBytes and
GetChars will return a long value, which represents the number of bytes or characters
returned. If you pass a null array to GetBytes or GetChars, the long value returned
will be the total number of bytes or characters in the BLOB. You can optionally specify
an index in the array as a starting position for the data being read.
Writing BLOB Values to a Database
You can write a binary large object (BLOB) to a
database as either binary or character data, depending on the type of field at your data
source. To write a BLOB value to your database, issue the appropriate INSERT or UPDATE
statement and pass the BLOB value as an input parameter. If your BLOB is stored as text,
such as a SQL Server text field, you can pass the BLOB as a string parameter. If the
BLOB is stored in binary format, such as a SQL Server image field, you can pass an array
of type byte as a binary parameter.
Example
The following code example adds employee information to the Employees table in
the Northwind database. A photo of the employee is read from a file and added to
the Photo field in the table, which is an image field. The Photo field is then read using GetBytes. Notice that the employee id
is accessed for the current row of data before the Photo, because the fields must be
accessed sequentially.
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Data.SqlClient;
using System.IO;
namespace Akadia.ReadWriteBlob
{
// Read and Write BLOB to/from Filesystem and
Database
public class BlobSample : System.Windows.Forms.Form
{
private System.Windows.Forms.PictureBox
pbxPhoto;
private System.ComponentModel.Container
components = null;
private System.Windows.Forms.Button
btnLoadPhoto;
private System.Windows.Forms.Button
btnAddEmp;
private System.Windows.Forms.Button
btnGetEmp;
private System.Windows.Forms.StatusBar
statusBar;
// My own private
Variables
private String _fname = null;
private SqlConnection _conn;
// Constructor
public BlobSample()
{
//
Initialize GUI
InitializeComponent();
//
Get Database Connection
_conn = new
SqlConnection("data source=XEON;"+
"initial catalog=Northwind;"+
"user id=sa;password=manager;");
}
// **** Read Image from
Filesystem and add it to the Database.
public void AddEmployee(
string plastName,
string pfirstName,
string ptitle,
DateTime phireDate,
int preportsTo,
string
photoFilePath)
{
//
Read Image into Byte Array from Filesystem
byte[] photo =
GetPhoto(photoFilePath);
//
Construct INSERT Command
SqlCommand addEmp = new
SqlCommand(
"INSERT INTO Employees ("+
"LastName,FirstName,Title,HireDate,ReportsTo,Photo) "+
"VALUES(@LastName,@FirstName,@Title,@HireDate,@ReportsTo,@Photo)",_conn);
addEmp.Parameters.Add("@LastName", SqlDbType.NVarChar, 20).Value = plastName;
addEmp.Parameters.Add("@FirstName", SqlDbType.NVarChar, 10).Value = pfirstName;
addEmp.Parameters.Add("@Title", SqlDbType.NVarChar, 30).Value =
ptitle;
addEmp.Parameters.Add("@HireDate", SqlDbType.DateTime).Value
= phireDate;
addEmp.Parameters.Add("@ReportsTo",
SqlDbType.Int).Value =
preportsTo;
addEmp.Parameters.Add("@Photo", SqlDbType.Image,
photo.Length).Value = photo;
//
Open the Connection and INSERT the BLOB into the Database
_conn.Open();
addEmp.ExecuteNonQuery();
_conn.Close();
}
// **** Read Image into
Byte Array from Filesystem
public static byte[] GetPhoto(string
filePath)
{
FileStream fs = new
FileStream(filePath, FileMode.Open, FileAccess.Read);
BinaryReader br = new
BinaryReader(fs);
byte[] photo =
br.ReadBytes((int)fs.Length);
br.Close();
fs.Close();
return photo;
}
// **** Read BLOB from
the Database and save it on the Filesystem
public void GetEmployee(string
plastName,string pfirstName)
{
SqlCommand getEmp = new
SqlCommand(
"SELECT EmployeeID, Photo "+
"FROM Employees "+
"WHERE LastName = @lastName "+
"AND FirstName = @firstName", _conn);
getEmp.Parameters.Add("@LastName", SqlDbType.NVarChar, 20).Value = plastName;
getEmp.Parameters.Add("@FirstName", SqlDbType.NVarChar, 10).Value = pfirstName;
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 emp_id =
"";
// The employee id to use in the file name.
//
Open the connection and read data into the DataReader.
_conn.Open();
SqlDataReader myReader =
getEmp.ExecuteReader(CommandBehavior.SequentialAccess);
while
(myReader.Read())
{
// Get the employee id, which must occur before getting the
employee.
emp_id = myReader.GetInt32(0).ToString();
// Create a file to hold the output.
fs = new FileStream("employee" + emp_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.
bw.Write(outbyte, 0, (int)retval);
bw.Flush();
// Close the output file.
bw.Close();
fs.Close();
}
//
Close the reader and the connection.
myReader.Close();
_conn.Close();
}
private void btnAddEmp_Click(object sender,
System.EventArgs e)
{
DateTime hireDate =
DateTime.Parse("2003.05.03");
AddEmployee("Mary","Jones","Software Engineer",hireDate,5,_fname);
statusBar.Text =
"Employee added to the Database";
}
private void btnGetEmp_Click(object sender,
System.EventArgs e)
{
GetEmployee("Mary","Jones");
statusBar.Text =
"Employee saved to Filesystem";
}
private void btnLoadPhoto_Click(object sender,
System.EventArgs e)
{
OpenFileDialog dlg = new
OpenFileDialog();
dlg.Title = "Open
Photo";
dlg.Filter = "Windows
Bitmap Files (*.bmp)|*.bmp"
+ "|All files (*.*)|*.*";
if (dlg.ShowDialog() ==
DialogResult.OK)
{
try
{
pbxPhoto.Image = new Bitmap(dlg.OpenFile());
_fname = dlg.FileName;
}
catch (Exception ex)
{
MessageBox.Show("Unable to load file: " + ex.Message);
}
}
dlg.Dispose();
}
.....
.....
// The main entry point for the application.
[STAThread]
static void Main()
{
Application.Run(new
BlobSample());
}
}
}
|