Working with SQL Server BLOB Data in .NET

No.of Views2652
Bookmarked0 times
Downloads 
Votes0
By  Geming Leader   On  16 Feb 2010 02:02:02
Tag : ADO.NET , How to
Binary Large Objects (BLOBs) are pieces of data that have -usually- exceptionally large size (such as pictures or audio tracks). These values stored in SQL Server in an image column.
emailbookmarkadd commentsprint

Images in this article missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at info@codegain.com

 
This article is also available in my blog, Just Like a Magic.
هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.
Code: Geming.Samples.Blob.zip

Binary Large Objects (BLOBs) are pieces of data that have -usually- exceptionally large size (such as pictures or audio tracks). These values stored in SQL Server in an image column.

Sometimes the term BLOB is also applied to large character data values, such as those stored in text or ntext columns.

Also you can store BLOB data in a binary column, but it doesn't take larger than 8000 bytes. And image columns are more flexible.

Working with BLOB data is a bit strange because:

  1. You don't know how much size will be the retrieved data.
  2. The data may be very large so we need to retrieve it in chunks.

Our example is fairly simple. This example stores files in a database (FileStore) and retrieves it by name. The example relies on a database that contains one table, MyFiles. And the table itself contains two columns one for filename (PK) and the other is an image column for the file itself.

Storing BLOB data

Storing BLOB data in a database is easiest part:

In order to run this code, you must add using statements to Sql.Data.SqlClient and System.IO.

// C# Code

static void StoreFile(string filename)
{
    SqlConnection connection = new SqlConnection
        ("Server=(local) ; " +
        "Initial Catalog = FileStore ; " +
        "Integrated Security = SSPI");

    SqlCommand command = new SqlCommand
        ("INSERT INTO MyFiles VALUES (@Filename, @Data)",
            connection);

    command.Parameters.AddWithValue("@Filename",
        Path.GetFileName(filename));
    command.Parameters.AddWithValue("@Data",
        File.ReadAllBytes(filename));

    connection.Open();

    command.ExecuteNonQuery();

    connection.Close();
}
' VB.NET Code

Sub StoreFile(ByVal filename As String)
    Dim connection As New SqlConnection( _
        "Server=(local) ; Initial Catalog = FileStore ; " & _
        "Integrated Security = SSPI")

    Dim command As New SqlCommand( _
        "INSERT INTO MyFiles VALUES " & _
        "(@Filename, @Data)", connection)

    command.Parameters.AddWithValue("@Filename", _
        Path.GetFileName(filename))
    command.Parameters.AddWithValue("@Data", _
        File.ReadAllBytes(filename))

    connection.Open()

    command.ExecuteNonQuery()

    connection.Close()
End Sub

Code explanation: First, we created a connection to the SQL Server database. And then, we created the SqlCommand object that will hold the T-SQL Insert statement. After that, we filled the command parameters with required values. Finally, we executed the command.

Well, for avoiding SQL-Injection attacks, it's recommended that you use parameters instead of hard-coding the argument. Moreover, you can't represent binary values as strings. Frankly, it's recommended using stored procedures instead of coding the commands.
It's highly recommended that you dispose disposable objects like SqlConnection and SqlCommand. Try encapsulating it in a using statement.

Retrieving BLOB data

Retrieving BLOB data is a bit complex than storing it. The following method demonstrates this:

// C# Code

static byte[] RetrieveFile(string filename)
{
    SqlConnection connection = new SqlConnection
        ("Server=(local) ; Initial Catalog = FileStore ; Integrated Security = SSPI");

    SqlCommand command = new SqlCommand
        ("SELECT * FROM MyFiles WHERE Filename=@Filename", connection);

    command.Parameters.AddWithValue("@Filename", filename);

    connection.Open();

    SqlDataReader reader = command.ExecuteReader(System.Data.CommandBehavior.SequentialAccess);

    reader.Read();

    MemoryStream memory = new MemoryStream();

    long startIndex = 0;
    const int ChunkSize = 256;
    while (true)
    {
        byte[] buffer = new byte[ChunkSize];

        long retrievedBytes = reader.GetBytes(1, startIndex, buffer, 0, ChunkSize);

        memory.Write(buffer, 0, (int)retrievedBytes);

        startIndex += retrievedBytes;

        if (retrievedBytes != ChunkSize)
            break;
    }

    connection.Close();

    byte[] data = memory.ToArray();

    memory.Dispose();

    return data;
}
' VB.NET Code
Function RetrieveFile(ByVal filename As String) As Byte()
    Dim connection As New SqlConnection( _
        "Server=(local) ; Initial Catalog = FileStore ; " & _
        "Integrated Security = SSPI")

    Dim command As New SqlCommand( _
        "SELECT * FROM MyFiles " & _
        "WHERE Filename=@Filename", connection)

    command.Parameters.AddWithValue("@Filename", filename)

    connection.Open()

    Dim reader As SqlDataReader = command.ExecuteReader _
        (System.Data.CommandBehavior.SequentialAccess)

    reader.Read()

    Dim memory As New MemoryStream()

    Dim startIndex As Long = 0
    Const ChunkSize As Integer = 256
    While (True)
        Dim buffer(ChunkSize) As Byte

        Dim retrievedBytes As Long = _
            reader.GetBytes(1, startIndex, buffer, 0, ChunkSize)

        memory.Write(buffer, 0, CInt(retrievedBytes))

        startIndex += retrievedBytes

        If (retrievedBytes <> ChunkSize) Then
            Exit While
        End If
    End While

    connection.Close()

    Dim data() As Byte = memory.ToArray()

    memory.Dispose()

    Return data
End Function

Code explanation: After connecting to the database and writing our query, we executed the query by calling ExecuteReader() method of the command object to get read-only forward-only pointer to the retrieved rows. By default, SqlDataReader reads entire rows -that can be gigabytes of data.- By specifying CommandBehavior.SequentialAccess, it reads the data sequentially in a given chunk size by calling the GetBytes() -or GetChars for BLOB textual data- method. Calling Read() of the SqlDataReader objects advances the pointer to the next row which is the first single row -if found- in our example. The GetBytes() method takes five arguments:

  1. The column index.
  2. The index of which to start reading.
  3. The buffer object that will keep current retrieved data.
  4. Index in buffer of which to begin writing t.
  5. The length (chunk size) of the data to retrieve.

It it worth mentioning that this method returns number of bytes retrieved. After calling this method we used a MemoryStream object to write all data retrieved to. Finally, we retrieve data by calling MemoryStream's ToArray() function. (I think the code is now clear)

It's not recommended using MemoryStream if the data is very huge.
SqlConnection, SqlCommand, SqlDataReader, and MemoryStream are all disposable objects. Because the MemoryStream object may contain the retrieved data it's highly recommended that you dispose it as soon as possible.

For a complete example download the sample project FileStore. This project uses a database for storing files and retrieving it. This database contains only one table, its definition is as follows:

FileStore File Table Definition

For creating the database, the project also inculdes a SQL Query file that contains the commands for creating it. Simply execute the file.Download Here

 
Sign Up to vote for this article
 
About Author
 
Geming Leader
Occupation-Software Engineer
Company-Just Like a Magic
Member Type-Expert
Location-Egypt
Joined date-30 Jul 2009
Home Page-http://WithDotNet.net
Blog Page-http://JustLikeAMagic.com
Independent software developer, trainer, and technical writer from Egypt born in 1991
 
 
Other popularSectionarticles
Comments
There is no comments for this articles.
Leave a Reply
Title:
Display Name:
Email:
(not display in page for the security purphase)
Website:
Message:
Please refresh your screen using Ctrl+F5
If you can't read this number refresh your screen
Please input the anti-spam code that you can read in the image.
^ Scroll to Top