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

Переводы статьи Переводы статьи
Код статьи: 317043 - Vizualiza?i produsele pentru care se aplic? acest articol.
Версия данной статьи для 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 по следующему адресу:
Obtaining BLOB Values from a Database
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconobtainingblobvaluesfromdatabase.asp

Свойства

Код статьи: 317043 - Последний отзыв: 28 июня 2004 г. - Revision: 2.1
Информация в данной статье относится к следующим продуктам.
  • Microsoft ADO.NET (included with the .NET Framework)
  • Microsoft ADO.NET 1.1
  • Microsoft Visual C# .NET 2002 Standard Edition
  • Microsoft Visual C# .NET 2003 Standard Edition
Ключевые слова: 
kbsystemdata kbsqlclient kbhowtomaster KB317043

Отправить отзыв

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com