|
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());
}
}
}
|