Virtual Developer Workshop: Containerized Development with Docker
Sometimes you need to store images in a database instead of as physical files. This sample application will show you how to build a Windows Forms interface that allows you to do the following:
- Browse for an image on your hard disk
- Load the selected image into a PictureBox control for viewing
- Save an image displayed in the PictureBox control to the database
- Select an image from a ListBox control, and load it from the database
The new concepts in this article center around the abstract Stream class and how it's used to convert an image file to and from the Image data type that SQL Server uses to store images. Be sure not to confuse the Image data type with the word image, as if to imply that only images can be stored therein. Rather, the Image data type can store anything as variable-length binary data.
A byte array is used to send data to an Image field. Thus, the main question is: How does one convert an image file—whether a JPEG, Bitmap, or other format—into an array of bytes? There are several ways to accomplish this in .NET. One of the easiest ways is to use a concrete implementation of the Stream class. A stream in .NET is essentially an abstraction of a sequence of bytes, whether these bytes came from a file, a TCP/IP socket, a database, or wherever. Stream classes allow you to work with binary data, reading and writing back and forth between streams and data structures (such as a byte array).
Once the image is converted to a byte array, it's saved to a database by using coding.
The first step you have to do is to create a Database table name it Pic, which should contain the two, fields 1: Name 2: Picture. The data Type of the Name field is (nVarChar) and data type of Picture is (Image) in Sql Server 2000.
Note:This Database should be in SQLS erver. I have included the database file in the zip file that you can attach in SQL Server databases. The name of database file is Images_Data.
Browsing for and Displaying an Image
The first task is to find an image on your hard disk. To do this, use an OpenFileDialog object in conjunction with a standard Button control. In the btnBrowse_Click event handler, you can see how this is done. The first few lines of code merely set properties of the OpenFileDialog object.
With OpenFileDialog1 .InitialDirectory = "C:\" .Filter = "All Files|*.*|Bitmaps|*.bmp|GIFs|*.gif|JPEGs|*.jpg" .FilterIndex = 2 End With
A pipe-delimited pair of file types is provided to determine the valid file types that can be accessed through the dialog box. Among other properties, you can also set FilterIndex to the default file type that you want to appear in the dialog box's Files Of Type menu. The index is not zero-based, so in this example, Bitmaps will appear as the default.
The dialog box is not actually opened until its ShowDialogmethod is called, which can be combined in an If/Then statement to check which button was pressed and perform follow-on tasks:
If OpenFileDialog1.ShowDialog() = DialogResult.OK Then With PictureBox1 .Image = Image.FromFile(Me.OpenFileDialog1.FileName) .SizeMode = PictureBoxSizeMode.CenterImage End With End If Me.Label1.Text = Me.OpenFileDialog1.FileName.ToString
Although an OpenFileDialog object contains an Open button instead of an OK button, there is no DialogResult enumeration for the Open button. Instead, use the OK enumeration. Once it's confirmed that the Open button has been clicked, properties of the PictureBox control are set. Notice how the Image property—which requires an object of type System.Drawing.Image—is assigned. The Image class is abstract and exposes a number of shared methods for working with images, one of which is FromFile. This method creates an Image object from a fully qualified path; although the OpenFileDialog.FileName property might lead you to think that it contains only the file name, it actually has the full path.
Now that your image file is represented by an Image object, you can use a stream to convert it to a byte array. In the btnSave_Click event handler, the first line of code creates a MemoryStream object:
Dim ms As New MemoryStream()
A MemoryStream object is simply a stream that uses memory as its backup store instead of some other medium. As a result, a MemoryStream object usually provides better performance. Streams are flexible. You could, for example, have used a FileStream object to open the image file directly and read it in. There are certainly numerous other ways, too. The implementation here, however, is simple and straightforward.
The MemoryStream is then passed as an argument to the Save method, another member of the Image class. You can optionally pass the image format—for example, by accessing the Image's read-only RawFormat property:
The actual byte array conversion comes in the next line. GetBuffer returns an array of unsigned bytes being held by the stream.
Dim arrImage() As Byte = ms.GetBuffer ms.Close() 'It is good to always close the stream rather than ' to leave it for the garbage collector
The last data-gathering task is to extract the filename from the full path; there is no need to store the entire path in the database:
Dim strFilename As String = _ lblFilePath.Text.Substring(lblFilePath.Text.LastIndexOf("\")"+"1)
This might look a bit complex and convoluted, but all you're doing is indicating that you want a substring of the full path that starts after the last backslash.
With the filename extracted and the image converted to a byte array, you're now ready to use the ADO.NET practices you've already learned to push these to the database.
Dim cnn As New SqlConnection(connectionString) Dim strSQL As String = _ "INSERT INTO Picture (Filename, Picture)" & _ "VALUES (@Filename, @Picture)" Dim cmd As New SqlCommand(strSQL, cnn) With cmd .Parameters.Add(New SqlParameter("@Filename", _ SqlDbType.NVarChar, 50)).Value = strFilename .Parameters.Add(New SqlParameter("@Picture", _ SqlDbType.Image)).Value = arrImage EndWith cnn.Open() cmd.ExecuteNonQuery() cnn.Close()
As you can see, at this point there is nothing new except the use of the SqlDbType.Image enumeration. Set the value of the @Picture parameter to the byte array, and execute the INSERT statement as you would with any other type of data.
Reading an image
From this point forward, you're essentially reversing the process. To display an image, you have to convert it from a byte array to an Image, and then assign it to the PictureBox.Image property:
Behind the ClickImagesInDatabase button, write this code:
Me.SqlConnection1.Open() Me.SqlDataAdapter1.Fill(Me.DataSet11.Pic) With Me.ListBox1 .DataSource = Me.DataSet11.Pic .DisplayMember = "Name" End With Me.SqlConnection1.Close()
Choose from the SelectedIndexChanged Event from the Listbox event and write the code in the subroutine body:
Private Sub ListBox1_SelectedIndexChanged(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles ListBox1.SelectedIndexChanged Dim arrayImage() As Byte = CType(Me.DataSet11.Tables(0).Rows(Me.ListBox1.SelectedIndex) _ ("Picture"), Byte()) Dim ms As New MemoryStream(arrayImage) With Me.PictureBox1 .Image = Image.FromStream(ms) .SizeMode = PictureBoxSizeMode.CenterImage End With End Sub
The SelectedIndex property of the ListBox control is used to to retrieve the contents of the associated Picture field in the DataSet object, which is then explicitly cast to a byte array. Following this, a MemoryStream is created by passing the byte array to its constructor. The last step is to invoke the shared FromStream method to convert the stream contents to an Image, and then assign this to the PictureBox.Image property.
Note: I have generated DataAdapter and Dataset via a wizard, but you can create the Dataset and DataAdapter objects explicitly and perform the required task.
Various Microsoft Press Books.