| Pictures inside a database | |
|
OLE object type format - take three! This next line of code stores the value of the Picture field to a file (BlobImage.dat) in the working directory. Assign this code in the OnCreate event for the form, start the project and remove the code.
Once we have the file, we can use some Hex editor to see it's content.
All this leaves us with nothing but to store the picture to a disk (as an ordinary binary file) and see what's inside it.
One nice thing with picture files (formats) is that all have some header that uniquely identifies the image. The JPG picture file starts with the, so called, SOI marker that has the value of $FFD8 hex.
ADOTable1Picture.SaveToFile('BlobImage.dat');

Would you believe this! MS Access stores the path of a linked OLE object as part of the object's definition in the OLE object field. Because the definition of OLE object storage is not documented (!? this is straight from MS) there is no way to know what gets written before the actual image data.
Think about this twice. First: we'll need to seek to the 'FFD8' and read the image from there. Second, the 'FFD8' might not always be at the same position in the file. Conclusion: we need a function that returns the position of the SOI marker for the JPG file stored as OLE object in an Access database.
The correct way - take four!
Provided with the Blob type field our function should return the position of the 'FFD8' string inside the ADOBlobStream. The ReadBuffer reads byte by byte from the stream. Each call to ReadBuffer moves the position of the stream by one. When two bytes together (as hex values) result in SOI marker the function returns the stream position. This is the function:
function JpegStartsInBlob
(PicField:TBlobField):integer;
var
bS : TADOBlobStream;
buffer : Word;
hx : string;
begin
Result := -1;
bS := TADOBlobStream.Create(PicField, bmRead);
try
while (Result = -1) and
(bS.Position + 1 < bS.Size) do
begin
bS.ReadBuffer(buffer, 1);
hx:=IntToHex(buffer, 2);
if hx = 'FF' then begin
bS.ReadBuffer(buffer, 1);
hx:=IntToHex(buffer, 2);
if hx = 'D8' then Result := bS.Position - 2
else if hx = 'FF' then
bS.Position := bS.Position-1;
end; //if
end; //while
finally
bS.Free
end; //try
end;
|
Once we have the position of the SOI marker we use it to seek to it in the ADOBlob stream.
uses jpeg;
...
procedure TForm1.btnShowImageClick(Sender: TObject);
var
bS : TADOBlobStream;
Pic : TJpegImage;
begin
bS := TADOBlobStream.Create
(AdoTable1Picture, bmRead);
try
bS.Seek(JpegStartsInBlob(AdoTable1Picture),
soFromBeginning);
Pic:=TJpegImage.Create;
try
Pic.LoadFromStream(bS);
ADOImage.Picture.Graphic:=Pic;
finally
Pic.Free;
end;
finally
bS.Free
end;
end;
|
Run the project and voila!

Who can now say that programming isn't FUN?
Note: in real code application we would have the code to read and display the image from the current row in the AfterScroll event of a TDataSet (that is in the ADOTable1AfterScroll event procedure). AfterScroll occurs after an application scrolls from one record to another.
Take five!
That's it for this chapter. You can now store and display all your favorite JPG pictures. In the last page of this article I have provided you with the entire code (form1's unit); all the data assignment is placed in the OnCreate event of the form. This ensures that all three components are correctly linked - you don't need to use the Object Inspector at design-time.
I agree, the chapter was not designed for beginners, but hey the World is cruel. Another thing: did you mentioned that at the end you don't know how to change (or add some new) picture in a table! We'll, that's whole another story!
Hm, ok I give up! Take a look at the code ... see the "SaveJpegToTable" procedure? This one does the job of placing a picture inside a database.
Next page > Project's Code > Page 1, 2, 3, 4, 5
DB Course Next Chapter >>
>>
Chapter 4: Data browsing

