Section 4: Finding the Beginning of JPEG in BLOB
OLE object type format - take three!
Now all we need to do is store the image to disk (as a plain binary file) and understand what's inside it.
All image files (formats) have file headers that uniquely identify the image. JPG image files start with a so-called SOI tag, whose hexadecimal value is $FFD8.
The following line of code stores the image field value into the relevant file (BlobImage.dat) in the working directory. Place this code in the form's OnCreate event and remove it after starting the project.
ADOTable1Picture.SaveToFile('BlobImage.dat');
Once we have this file. We can use the Hex editor to view its content.
Do you believe it? MS access stores the path to the connected OLE object in the OLE object field as part of the object definition. Because the storage definition of OLE objects is not documented (!? This comes directly from MS), there is no way of knowing what the actual image data will get before it is written.
Consider this question in two parts. First: we need to find 'FFD8' and start reading the image from there. Second: 'FFD8' cannot always be in the same position in the file. Conclusion: We need a function that returns the location of the SOI tag of a JPG file stored as an OLE object in the Access database.
The correct way - take four!
With a Blob type field provided, our function should return the position of the 'FFD8' string in the ADOBlobStream. ReadBuffer reads data from the stream byte by byte. Each call to ReadBuffer moves the position of the stream byte by byte. The function returns the position of the stream when two bytes together lead to the SOI marker. 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 location of the SOI marker, we can use it to find the location of the image 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, OK!
Now who can say that programming is not fun?
Note: In the real code program, we will add code to read and display the image from the current row in the AfterScroll event of TDataSet (it is in the ADOTable1AfterScroll event procedure). The AfterScroll event occurs when the application scrolls from one record to another.
Idea five!
That's what this chapter is about. Now you can store and display all the JPG images you are interested in. On the last page of this article, I will provide the complete code (form1 unit); all data arrangement is placed in the form's OnCreate event. This ensures that all three components are connected correctly - you don't need to use the Object Inspector when designing.
I admit, this chapter is not for beginners, but the world is a cruel place! Another thing: you notice that in the end you don't even know how to change (or add some new) pictures to the table! Yes, that’s a whole other story!