В данной статье рассматривается использование инструкций READTEXT и UPDATETEXT сервера Microsoft SQL Server для чтения и записи данных в поля типа BLOB таблицы базы данных.
Ограничения, существующие в сети, могут приводить к тому, что файл, хранящийся в поле типа BLOB, невозможно считать целиком. Такие файлы можно разбить на фрагменты, которые необходимо считывать по отдельности, а затем объединять в файл. Однако методы GetChunk и AppendChunk поставщиков данных ADO.NET нельзя применять к объектам типа Recordset в DAO (Data Access Object) и ADO (ActiveX Data Objects). В этой статье описан альтернативный способ извлечения данных.
Примечания
Данная статья содержит примеры для поставщиков данных SqlClient Data Provider и OLE DB .NET Data Provider. Единственное отличие между ними (за исключением имени класса) заключается в строках подключения и объявлении параметров SQL. При использовании инструкций READTEXT и UPDATETEXT применяется аналогичный подход.
В таблице Categories учебной базы данных Northwind отсутствует запись со значением «Test». Необходимо с помощью обозревателя серверов (или аналогичного средства) добавить запись, в которой полю CategoryName присвоено значение Test. Если после изучения приведенных примеров нужно будет удалить эту запись, введите следующие команды в программе SQL Query Analyzer и нажмите клавишу F5:
use Northwind
delete from Categories where CategoryName = 'Test'
Введите следующую команду и нажмите клавишу F5, чтобы использовать по умолчанию базу данных Northwind:
use Northwind
Введите следующую команду и нажмите клавишу F5, чтобы вставить новую запись в таблицу Categories базы данных Northwind:
Insert into categories(categoryname) values ('Test')
Примечание. Эту запись следует добавлять только в том случае, если примеры, приведенные в данной статье, не должны изменять данные в таблице.
Запустите Visual Studio .NET и создайте проект Visual C# .NET Windows Application.
Поместите следующие две строки кода в начало файла Form1.cs, чтобы добавить ссылку на System.Data.SQLClient и System.Data.OleDb:
using System.Data.SqlClient;
using System.Data.OleDb;
Поместите на форму Form1 четыре кнопки. Присвойте свойству Text этих кнопок значения SQLBlob2File, OlDbBlob2File, File2OleDbBlob и File2SqlBlob соответственно.
В объявление класса Form1 добавьте следующие строки:
string destfilepath;
string sourcefilepath;
Добавьте следующий код к обработчику события Load данной формы:
Вызовите процедуры событий Click для каждой кнопки:
// Click event for the button labeled SqlBlob2File.
SqlChunkBlob2File(destfilepath);
// Click event for the button labeled OLDbBlob2File.
OlDbChunkBlob2File(destfilepath);
// Click event for the button labeled File2OleDbBlob.
ChunkFile2OleDbBlob(sourcefilepath);
//Click event for the button labeled File2SqlBlob.
ChunkFile2SqlBlob(sourcefilepath);
Поместите в файл Form1 следующие четыре функции:
public void SqlChunkBlob2File(string DestFilePath)
{
try
{
int PictureCol = 0; // position of Picture column in DataReader
int BUFFER_LENGTH = 32768; // chunk size
SqlConnection cn = new SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind;");
// Make sure Photo is non-NULL and return TEXTPTR to it.
SqlCommand cmdGetPointer = new SqlCommand("SELECT @Pointer=TEXTPTR(Picture), @Length=DataLength(Picture) FROM Categories WHERE CategoryName='Test'", cn);
SqlParameter PointerOutParam = cmdGetPointer.Parameters.Add("@Pointer", SqlDbType.VarBinary, 100);
PointerOutParam.Direction = ParameterDirection.Output;
SqlParameter LengthOutParam = cmdGetPointer.Parameters.Add("@Length", SqlDbType.Int);
LengthOutParam.Direction = ParameterDirection.Output;
cn.Open();
cmdGetPointer.ExecuteNonQuery();
if(PointerOutParam.Value == null)
{
cn.Close();
// Add code to handle NULL BLOB.
return;
}
// Set up READTEXT command, parameters, and open BinaryReader.
SqlCommand cmdReadBinary = new SqlCommand("READTEXT Categories.Picture @Pointer @Offset @Size HOLDLOCK", cn);
SqlParameter PointerParam = cmdReadBinary.Parameters.Add("@Pointer", SqlDbType.Binary, 16);
SqlParameter OffsetParam = cmdReadBinary.Parameters.Add("@Offset", SqlDbType.Int);
SqlParameter SizeParam = cmdReadBinary.Parameters.Add("@Size", SqlDbType.Int);
SqlDataReader dr;
System.IO.FileStream fs = new System.IO.FileStream(DestFilePath, System.IO.FileMode.Create, System.IO.FileAccess.Write);
int Offset= 0;
OffsetParam.Value = Offset;
Byte []Buffer = new Byte[BUFFER_LENGTH ];
// Read buffer full of data and write to the file stream.
do
{
PointerParam.Value = PointerOutParam.Value;
// Calculate buffer size - may be less than BUFFER_LENGTH for last block.
if( (Offset + BUFFER_LENGTH) >= System.Convert.ToInt32(LengthOutParam.Value))
SizeParam.Value = System.Convert.ToInt32(LengthOutParam.Value) - Offset;
else SizeParam.Value = BUFFER_LENGTH;
dr = cmdReadBinary.ExecuteReader(CommandBehavior.SingleResult);
dr.Read();
dr.GetBytes(PictureCol, 0, Buffer, 0, System.Convert.ToInt32(SizeParam.Value));
dr.Close();
fs.Write(Buffer, 0, System.Convert.ToInt32(SizeParam.Value));
Offset += System.Convert.ToInt32(SizeParam.Value);
OffsetParam.Value = Offset;
}while(Offset < System.Convert.ToInt32(LengthOutParam.Value));
fs.Close();
cn.Close();
}
catch(SqlException ex)
{
MessageBox.Show (ex.Message);
}
}
public void OleDbChunkBlob2File(string DestFilePath)
{
try
{
int PictureCol= 0; // Position of picture column in DataReader.
int BUFFER_LENGTH = 32768;// Chunk size.
OleDbConnection cn = new OleDbConnection("Provider=SQLOLEDB;Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind;");
// Make sure Photo is non-NULL and return TEXTPTR to it.
OleDbCommand cmdGetPointer = new OleDbCommand("SELECT ?=TEXTPTR(Picture), ?=DataLength(Picture) FROM Categories WHERE CategoryName='Test'", cn);
OleDbParameter PointerOutParam = cmdGetPointer.Parameters.Add("@Pointer", OleDbType.VarBinary, 100);
PointerOutParam.Direction = ParameterDirection.Output;
OleDbParameter LengthOutParam = cmdGetPointer.Parameters.Add("@Length", OleDbType.Integer);
LengthOutParam.Direction = ParameterDirection.Output;
cn.Open();
cmdGetPointer.ExecuteNonQuery();
if(PointerOutParam.Value == null)
{
cn.Close();
// Add code to deal with NULL BLOB.
return;
}
// Set up READTEXT command, parameters, and open BinaryReader.
OleDbCommand cmdReadBinary = new OleDbCommand("READTEXT Categories.Picture ? ? ? HOLDLOCK", cn);
OleDbParameter PointerParam = cmdReadBinary.Parameters.Add("@Pointer", OleDbType.Binary, 16);
OleDbParameter OffsetParam = cmdReadBinary.Parameters.Add("@Offset", OleDbType.Integer);
OleDbParameter SizeParam = cmdReadBinary.Parameters.Add("@Size", OleDbType.Integer);
OleDbDataReader dr;
System.IO.FileStream fs = new System.IO.FileStream(DestFilePath, System.IO.FileMode.Create, System.IO.FileAccess.Write);
int Offset= 0;
OffsetParam.Value = Offset;
Byte[] Buffer = new Byte[BUFFER_LENGTH];
//Read buffer full of data and write to the file stream.
do
{
PointerParam.Value = PointerOutParam.Value;
// Calculate buffer size - may be less than BUFFER_LENGTH for last block.
if((Offset + BUFFER_LENGTH) >= System.Convert.ToInt32(LengthOutParam.Value))
SizeParam.Value = System.Convert.ToInt32(LengthOutParam.Value) - Offset;
else SizeParam.Value = BUFFER_LENGTH;
dr = cmdReadBinary.ExecuteReader(CommandBehavior.SingleResult);
dr.Read();
dr.GetBytes(PictureCol, 0, Buffer, 0, System.Convert.ToInt32(SizeParam.Value));
dr.Close();
fs.Write(Buffer, 0, System.Convert.ToInt32(SizeParam.Value));
Offset += System.Convert.ToInt32(SizeParam.Value);
OffsetParam.Value = Offset;
}while( Offset < System.Convert.ToInt32(LengthOutParam.Value));
fs.Close();
cn.Close();
}
catch(OleDbException ex)
{
MessageBox.Show (ex.Message);
}
}
Нажмите клавишу F5, чтобы запустить программу. Нажмите кнопку File2OleDbBlob, чтобы загрузить образ в базу данных.
Следующие функции используют DataReader и команду READTEXT сервера SQL Server, чтобы считать значение типа BLOB в набор строк, имеющий одну строку и один столбец. При этом используются две команды, первая из которых определяет размер поля типа BLOB и указатель на его размещение, а вторая выполняет команду READTEXT. Команда READTEXT считывает фрагмент данных в массив байтов и увеличивает значение переменной Offset. Массив байтов записывается на диск с помощью объекта System.IO.Filesream.
Следующие функции используют команду UPDATETEXT сервера SQL Server и объекты Command и Parameter, чтобы сохранить фрагменты данных из массива байтов в поле типа BLOB. При использовании этого метода поле типа BLOB не может быть пустым, поэтому перед получением значения TEXTPTR в это поле помещается один байт данных. При первом выполнении команды UPDATETEXT переменной DeleteParam.Value присваивается значение 1. При этом байт данных, ранее записанный в поле типа BLOB, удаляется. Команда UPDATETEXT выполняется несколько раз. При каждом выполнении значение переменной Offset увеличивается на величину, равную размеру буфера.
private void ChunkFile2SqlBlob(string SourceFilePath)
{
try
{
int BUFFER_LENGTH = 32768; // Chunk size.
SqlConnection cn = new SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind;");
// Make sure Photo is non-NULL and return TEXTPTR to it.
SqlCommand cmdGetPointer = new SqlCommand("SET NOCOUNT ON;UPDATE Categories SET Picture = 0x0 WHERE CategoryName='Test';" +
"SELECT @Pointer=TEXTPTR(Picture) FROM Categories WHERE CategoryName='Test'", cn);
SqlParameter PointerOutParam = cmdGetPointer.Parameters.Add("@Pointer", SqlDbType.VarBinary, 100);
PointerOutParam.Direction = ParameterDirection.Output;
cn.Open();
cmdGetPointer.ExecuteNonQuery();
// Set up UPDATETEXT command, parameters, and open BinaryReader.
SqlCommand cmdUploadBinary = new SqlCommand("UPDATETEXT Categories.Picture @Pointer @Offset @Delete WITH LOG @Bytes", cn);
SqlParameter PointerParam = cmdUploadBinary.Parameters.Add("@Pointer", SqlDbType.Binary, 16);
SqlParameter OffsetParam= cmdUploadBinary.Parameters.Add("@Offset", SqlDbType.Int);
SqlParameter DeleteParam = cmdUploadBinary.Parameters.Add("@Delete", SqlDbType.Int);
DeleteParam.Value = 1; // delete 0x0 character
SqlParameter BytesParam = cmdUploadBinary.Parameters.Add("@Bytes", SqlDbType.Binary, BUFFER_LENGTH);
System.IO.FileStream fs = new System.IO.FileStream(SourceFilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);
System.IO.BinaryReader br = new System.IO.BinaryReader(fs);
int Offset = 0;
OffsetParam.Value = Offset;
// Read buffer full of data and execute UPDATETEXT statement.
Byte [] Buffer = br.ReadBytes(BUFFER_LENGTH);
while(Buffer.Length > 0)
{
PointerParam.Value = PointerOutParam.Value;
BytesParam.Value = Buffer;
cmdUploadBinary.ExecuteNonQuery();
DeleteParam.Value = 0; //Do not delete any other data.
Offset += Buffer.Length;
OffsetParam.Value = Offset;
Buffer = br.ReadBytes(BUFFER_LENGTH);
}
br.Close();
fs.Close();
cn.Close();
}
catch(SqlException ex)
{
MessageBox.Show (ex.Message);
}
}
public void ChunkFile2OleDbBlob(string SourceFilePath)
{
try
{
int BUFFER_LENGTH = 32768; // chunk size
OleDbConnection cn = new OleDbConnection("Provider=SQLOLEDB;Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind;");
// Make sure Photo is non-NULL and return TEXTPTR to it.
OleDbCommand cmdGetPointer = new OleDbCommand("SET NOCOUNT ON;UPDATE Categories SET Picture = 0x0 WHERE CategoryName='Test';" +
"SELECT ?=TEXTPTR(Picture) FROM Categories WHERE CategoryName='Test'", cn);
OleDbParameter PointerOutParam = cmdGetPointer.Parameters.Add("@Pointer", OleDbType.VarBinary, 100);
PointerOutParam.Direction = ParameterDirection.Output;
cn.Open();
cmdGetPointer.ExecuteNonQuery();
// Set up UPDATETEXT command, parameters, and open BinaryReader.
OleDbCommand cmdUploadBinary = new OleDbCommand("UPDATETEXT Categories.Picture ? ? ? WITH LOG ?", cn);
OleDbParameter PointerParam = cmdUploadBinary.Parameters.Add("@Pointer", OleDbType.Binary, 16);
OleDbParameter OffsetParam = cmdUploadBinary.Parameters.Add("@Offset", OleDbType.Integer);
OleDbParameter DeleteParam = cmdUploadBinary.Parameters.Add("@Delete", OleDbType.Integer);
DeleteParam.Value = 1; // delete 0x0 character
OleDbParameter BytesParam = cmdUploadBinary.Parameters.Add("@Bytes", OleDbType.Binary, BUFFER_LENGTH);
System.IO.FileStream fs = new System.IO.FileStream(SourceFilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);
System.IO.BinaryReader br = new System.IO.BinaryReader(fs);
int Offset= 0;
OffsetParam.Value = Offset;
// Read buffer full of data and execute UPDATETEXT statement.
Byte[] Buffer = br.ReadBytes(BUFFER_LENGTH);
while(Buffer.Length > 0)
{
PointerParam.Value = PointerOutParam.Value;
BytesParam.Value = Buffer;
cmdUploadBinary.ExecuteNonQuery();
DeleteParam.Value = 0;// Do not delete any other data.
Offset += Buffer.Length;
OffsetParam.Value = Offset;
Buffer = br.ReadBytes(BUFFER_LENGTH);
}
br.Close();
fs.Close();
cn.Close();
}
catch(OleDbException ex)
{
MessageBox.Show (ex.Message);
}
}
Примечания
Примеры, приведенные в данной статье, не предназначены для использования с полями типа LongVarChar или LongVarWChar.
Измените строку подключения и параметры SQL таким образом, чтобы они соответствовали используемому серверу. Добавьте процедуру, проверяющую, возвращена ли в ответ на запрос хотя бы одна запись.
Команды READTEXT и UPDATETEXT являются командами сервера Microsoft SQL Server. Другие системы управления базами данных могут иметь команды, выполняющие аналогичные функции.
Спасибо! Благодаря вашему отзыву мы сможем сделать справочные материалы еще лучше. Чтобы воспользоваться дополнительными возможностями поддержки, посетите домашнюю страницу центра справки и поддержки.