Чтение и запись файла в поля типа BLOB путем разделения на фрагменты с помощью ADO.NET и Visual C# .NET

Версия данной статьи для Microsoft Visual Basic .NET: 317034 .
Версия данной статьи для Microsoft Visual C++ .NET: 317044 .

Аннотация

В данной статье рассматривается использование инструкций 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'
К началу статьи

Требования

Ниже перечислены требуемые параметры оборудования и сети, а также необходимое программное обеспечение, пакеты обновления, знания и опыт.
  • Microsoft Windows 2000 Professional, Windows 2000 Server, Windows 2000 Advanced Server или Microsoft Windows NT 4.0 Server.
  • Microsoft Visual Studio .NET.
  • Microsoft SQL Server 7.0 (или более поздняя версия).
Для понимания материала, представленного в статье, необходимо знание синтаксиса и основ использования ADO.NET.

К началу статьи

Создание проекта и добавление кода

  1. Запустите приложение SQL Query Analyzer.
  2. Введите следующую команду и нажмите клавишу F5, чтобы использовать по умолчанию базу данных Northwind:
    use Northwind
  3. Введите следующую команду и нажмите клавишу F5, чтобы вставить новую запись в таблицу Categories базы данных Northwind:
    Insert into categories(categoryname) values ('Test')
    Примечание. Эту запись следует добавлять только в том случае, если примеры, приведенные в данной статье, не должны изменять данные в таблице.
  4. Запустите Visual Studio .NET и создайте проект Visual C# .NET Windows Application.
  5. Поместите следующие две строки кода в начало файла Form1.cs, чтобы добавить ссылку на System.Data.SQLClient и System.Data.OleDb:
    using System.Data.SqlClient;
    using System.Data.OleDb;
  6. Поместите на форму Form1 четыре кнопки. Присвойте свойству Text этих кнопок значения SQLBlob2File, OlDbBlob2File, File2OleDbBlob и File2SqlBlob соответственно.
  7. В объявление класса Form1 добавьте следующие строки:
    string destfilepath;
    string sourcefilepath;
  8. Добавьте следующий код к обработчику события Load данной формы:
    destfilepath = @"c:\mytest.bmp";
    sourcefilepath = @"c:\windows\coffee bean.bmp";
  9. Вызовите процедуры событий 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);
  10. Поместите в файл 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);
    }
    }
  11. Нажмите клавишу F5, чтобы запустить программу. Нажмите кнопку File2OleDbBlob, чтобы загрузить образ в базу данных.
К началу статьи

Считывание фрагментов из полей типа BLOB

Следующие функции используют DataReader и команду READTEXT сервера SQL Server, чтобы считать значение типа BLOB в набор строк, имеющий одну строку и один столбец. При этом используются две команды, первая из которых определяет размер поля типа BLOB и указатель на его размещение, а вторая выполняет команду READTEXT. Команда READTEXT считывает фрагмент данных в массив байтов и увеличивает значение переменной Offset. Массив байтов записывается на диск с помощью объекта System.IO.Filesream.К началу статьи

Запись фрагментов в поля типа BLOB

Следующие функции используют команду 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. Другие системы управления базами данных могут иметь команды, выполняющие аналогичные функции.
К началу статьи

Ссылки

Дополнительные сведения о чтении и записи данных без разбиения на фрагменты см. в следующих статьях базы данных Майкрософт.
316887 Чтение и запись файла в поля типа BLOB с помощью ADO.NET и Visual Basic .NET
317017 Чтение и запись файла в поля типа BLOB с помощью ADO.NET и Visual C++ .NET
Дополнительные сведения о работе с полями типа BLOB в ADO.NET см. на веб-узле MSDN по следующему адресу: К началу статьи
Свойства

Номер статьи: 317043 — последний просмотр: 29 апр. 2008 г. — редакция: 1

Отзывы и предложения