|
I have an Access database, that stores image files in a field of one of its tables. Now, I want to show those images in a PictureBox. I use the following code for it :
Private Sub ShowPic() 'Connection String is properly initialized in the form's constructor. Dim con As New OleDb.OleDbConnection(ConnectionString) con.Open()
Dim query As String = "Select Photo from Member where MemberID='F23';" Dim command As New OleDb.OleDbCommand(query, con)
Dim arrImage As Byte() = command.ExecuteScalar() Dim ms As New System.IO.MemoryStream(arrImage)
'The following command generates an exception PictureBox1.Image = Drawing.Image.FromStream(ms)
ms.Close() con.Close() End Sub
Now the above code generates an exception "Parameter is not valid", when I try to assign the stream to the PictureBox.
Can someone figure out why??? And what's the solution??? Also, if I want to store a PictureBox image back to an Access database, then what should I do???
Remember, Access stores images as OLE objects that can be linked or embedded, and my database has only embedded images, thus they are stored with the database itself. I retrieved the image & checked out the Type of the data returned. The image is returned as a byte array, System.Byte[]. But how can I pass a System.Byte[] array back to the Access database.
| | Rahul Singla Wednesday, January 17, 2007 4:22 PM | As Hans says images stored as OLE objects in Access are OLE object data stored with them. I found a couple of references to the following code when discussing loading images from the Photo column of the Employees table of the Northwind sample Access database:
byte[] blob = (byte[]) (ds.Tables["employees"].Rows[0]["photo"]); _stream.Write (blob, 78, blob.Length -78); // hack's out the old header info _bitmap = new Bitmap (stream);
...but, I haven't tried it... | | Peter Ritchie Friday, January 19, 2007 1:32 PM | I don't know how consistent the magic number 78 is... I would suggest not using an OLE object and just store binary data, for example (aircode):
To save an image object MemoryStream stream = new MemoryStream(); image.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp); byte[] content = stream.ToArray();
using(OleDbCommand insert = new OleDbCommand( "insert into myTable ([picture]) values (?)",con)) { OleDbParameter imageParameter = insert.Parameters.Add("@image", OleDbType.Binary); imageParameter.Value = content; imageParameter.Size = content.Length; insert.ExecuteNonQuery(); }
then to load it...
Byte[] content = (Byte[])(ds.Tables["MyTable"].Rows[0]["picture"]); MemoryStream stream = new MemoryStream(content); Bitmap image = new Bitmap(stream); | | Peter Ritchie Friday, January 19, 2007 4:09 PM | Well, how can we just forget or ignore Peter's code given above to insert & fetch binary objects into the database (instead of typed images), to completely circumvent the problem of headers!!!
I am giving an adaptation of the above code in VB...
Dim con As New OleDb.OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=db1.mdb;") con.Open()
'INSERT Binary data Dim stream As New IO.MemoryStream
Dim sample As Image sample = Image.FromFile("qq.jpg")
sample.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp) Dim content() As Byte = stream.ToArray()
Dim insert As New OleDb.OleDbCommand("INSERT INTO Table1 ([pic]) values (?)", con)
Dim imageParameter As OleDb.OleDbParameter = _ insert.Parameters.Add("@image", OleDb.OleDbType.Binary) imageParameter.Value = content imageParameter.Size = content.Length insert.ExecuteNonQuery()
'Fetch & display binary data Dim command As New OleDb.OleDbCommand("SELECT pic from Table1 WHERE id=11;", con) Dim blob() As Byte = command.ExecuteScalar()
stream.Write(blob, 0, blob.Length) PictureBox1.Image = Image.FromStream(stream)
con.Close()
There's one warning though... The above code ran successfully for me, but the data fetched back was a bit distorted. You can check if that happens to you tooo...
| | Rahul Singla Saturday, June 02, 2007 5:11 PM | If your byte data is not a valid bitmap format then you'll get that exception. | | Peter Ritchie Wednesday, January 17, 2007 8:41 PM | But I just inserted that image in the Database myself, using the 'Insert Object' menu item of Access itself. So, can you point out why is that error being generated (How come the format can be invalid, when the image is inserted from a file by Access itself)???
Also, how can I send the byte arrays from the application to the database???
| | Rahul Singla Thursday, January 18, 2007 11:06 AM | Someone please help me out... Its really urgent, I need to show the images from the access database... I also tried fetching the images from the NorthWind's Categories database, but again the same exception was raised, so I guess invalid format is not the actual culprit. So, which one is it???
| | Rahul Singla Friday, January 19, 2007 10:29 AM | When you insert an image with Access' Insert Object, Access puts some kind of OLE header data in the column to make the object identifiable as an image. When you read the column, you'll see that header data. Image.FromStream() sees it too and blows. No idea how to strip that header data, it is rather off topic for the Windows Forms forum. You might have a small chance at one of the Data related forums here. Your best bet is an Office or Access development newsgroup at www.microsoft.com/communities
| | nobugz Friday, January 19, 2007 10:54 AM | As Hans says images stored as OLE objects in Access are OLE object data stored with them. I found a couple of references to the following code when discussing loading images from the Photo column of the Employees table of the Northwind sample Access database:
byte[] blob = (byte[]) (ds.Tables["employees"].Rows[0]["photo"]); _stream.Write (blob, 78, blob.Length -78); // hack's out the old header info _bitmap = new Bitmap (stream);
...but, I haven't tried it... | | Peter Ritchie Friday, January 19, 2007 1:32 PM | Wow, that worked. Thank you very much Peter... You've been a great help.
So, Access inserts 78 bytes of header information with the OLE object... That's fine, but could I be sure that it will always be 78 bytes & I hard-code this value in the application, or there is some specific way of finding the header length???
Also, how can I send an image from my application as a byte array to the database???
| | Rahul Singla Friday, January 19, 2007 1:57 PM | I don't know how consistent the magic number 78 is... I would suggest not using an OLE object and just store binary data, for example (aircode):
To save an image object MemoryStream stream = new MemoryStream(); image.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp); byte[] content = stream.ToArray();
using(OleDbCommand insert = new OleDbCommand( "insert into myTable ([picture]) values (?)",con)) { OleDbParameter imageParameter = insert.Parameters.Add("@image", OleDbType.Binary); imageParameter.Value = content; imageParameter.Size = content.Length; insert.ExecuteNonQuery(); }
then to load it...
Byte[] content = (Byte[])(ds.Tables["MyTable"].Rows[0]["picture"]); MemoryStream stream = new MemoryStream(content); Bitmap image = new Bitmap(stream); | | Peter Ritchie Friday, January 19, 2007 4:09 PM | Mind Blowing.... It would have taken me quite some time to write that code coz I would have tried to convert the byte array to a string somehow to send it to the database.... I did not think of using the Binary Data format... I sincerely thank you for your feedback Peter!!!!  | | Rahul Singla Friday, January 19, 2007 5:11 PM | I want to display jpeg picture instead of bitmap in a pictureBox from the Access databasewhere it has beeninserted as OLE. How can I do that? I am getting an error message, "parameter is not valid". Iam usingvb.net code not C#. Thanks. | | VB.NET Learner Tuesday, May 29, 2007 5:47 PM | The code you required is as follows:
Dim con As New OleDb.OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=db1.mdb;") con.Open()
Dim command As New OleDb.OleDbCommand("SELECT pic from Table1 WHERE id=5;", con) Dim blob() As Byte = command.ExecuteScalar()
Dim i As Integer For i = 0 To 1000 Dim stream As New IO.MemoryStream(blob, i, blob.Length - i) Try PictureBox1.Image = Image.FromStream(stream) Exit For Catch ex As Exception
End Try Next
con.Close()
I know this is not a very robust solution. The problem lies in the fact that Access embeds a jpeg image as a package, and an arbitrary number of bytes as a header of the image. Atleast I currently dont know the algo it uses to calculate the header information for a package object. So, I dont know the number of bytes to strip off the image header for which I have used the loop.
An option you can use is to embed a bitmap image into your database, for which the header information is fixed, 78 bytes. I had a similar problem earlier, which I sought to get cleared at Microsoft's Access community. The discussion took place at the following link, which is not currently working: http://msdn.microsoft.com/newsgroups/default.aspx?dg=microsoft.public.data.oledb&mid=d63300af-82f2-44ef-8b3c-e074ec156cd6
So, you can use http://www.microsoft.com/office/community/en-us/FlyoutOverview.mspx to put your own question as to how the header infomation for an object is calculated. 1) Ei
| | Rahul Singla Wednesday, May 30, 2007 5:34 PM | There are two more things I would like to add: 1) When you double click on a cell in an Access table storing any kind of image (bmp or jpeg), Access opens it in your default image viewer. That means it can correctly strip-off the header information & recognize the start of the image data. The length of the header must obviously be stored somewhere in the header itself, or the end of the header must be indicated with some flag. I tried to quickly analyze the header of some sample jpeg images I stored in an Access database. I found some patterns to distinguish header fields, like Access inserts 4 consecutive 255 values to demarcate one particular header field, although I could not quite make out the purpose of the field. One particular observation you might be interested in is that for all my sample images, the header terminated with successive 0,255 byte values. I cannot guarantee that to be universally applicable, but that is what I noticed. You can query further about the header at the Access community.
2) It is not advisable to embed an image in a database, especially in an Access database. Because, images being stored as blobs (binary large objects), your database size will quickly swell as you add images to it. It will hamper the use of the database for any serious use, like deploying it on a network. A recommended approach is to store the images in a folder on the file system, & store the names of the image file in the database instead. This is both efficient & maintainable. In fact, Access itself allows you to link in objects in this way in the database. The only thing you have to make sure is to somehow generate unique file-names for images being stored on the file-system, to prevent accidental over-writing. You can use some sort of a running counter for that approach.
This is the approach I followed for my database applcation, which is now successfully deployed on a network!!!
| | Rahul Singla Thursday, May 31, 2007 4:46 PM | I'm trying to do something similar using C#, ASP.NET and MS-SQL Server... have a look at this article if you go down that route... http://www.developerfusion.co.uk/show/5223/seems to be a nice way of doing it if I could get it to work.
If you discover the true length of a jpeg header, can you post it - there must be a list or something somewhere on this internet thing!!
Thanks,
Chris | | Humanscar Friday, June 01, 2007 10:11 AM | Thanks a lot Rahul | | VB.NET Learner Friday, June 01, 2007 6:57 PM | Well, how can we just forget or ignore Peter's code given above to insert & fetch binary objects into the database (instead of typed images), to completely circumvent the problem of headers!!!
I am giving an adaptation of the above code in VB...
Dim con As New OleDb.OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=db1.mdb;") con.Open()
'INSERT Binary data Dim stream As New IO.MemoryStream
Dim sample As Image sample = Image.FromFile("qq.jpg")
sample.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp) Dim content() As Byte = stream.ToArray()
Dim insert As New OleDb.OleDbCommand("INSERT INTO Table1 ([pic]) values (?)", con)
Dim imageParameter As OleDb.OleDbParameter = _ insert.Parameters.Add("@image", OleDb.OleDbType.Binary) imageParameter.Value = content imageParameter.Size = content.Length insert.ExecuteNonQuery()
'Fetch & display binary data Dim command As New OleDb.OleDbCommand("SELECT pic from Table1 WHERE id=11;", con) Dim blob() As Byte = command.ExecuteScalar()
stream.Write(blob, 0, blob.Length) PictureBox1.Image = Image.FromStream(stream)
con.Close()
There's one warning though... The above code ran successfully for me, but the data fetched back was a bit distorted. You can check if that happens to you tooo...
| | Rahul Singla Saturday, June 02, 2007 5:11 PM | Thanks a lot...This really solved my problem.
Siva | | Siva Chirravuri Tuesday, June 05, 2007 11:19 AM |
|