第四節在BLOB中尋找JPEG的開端
OLE物件類型格式—思路三(OLE object type format - take three!)
現在所有我們需要做的是將圖片儲存到磁碟(存為普通的二進位檔案)並了解它裡門的內容是什麼。
所有的圖片檔案(格式)都有用來唯一的識別影像的檔案頭。 JPG圖片檔案以所謂的SOI標記開始,該標記的十六進位值是$FFD8。
下面一行程式碼儲存圖片欄位值到工作目錄的相關檔案(BlobImage.dat)。在表單的OnCreate事件中放置這條程式碼,開始工程以後再移除程式碼。
ADOTable1Picture.SaveToFile('BlobImage.dat');
一旦我們有了這個文件。我們就可以使用Hex editor看它的內容。
你相信嗎? MS access把連接的OLE物件的路徑當作物件定義的一部分儲存在OLE物件欄位中。因為OLE物件的儲存定義沒有被文件化(!?這直接來自於MS),所以沒有辦法知道真正的圖像資料被寫之前能得到什麼。
分兩個部分來考慮這個問題。第一:我們需要找到'FFD8'並從那裡開始讀取影像。第二:'FFD8'不可能總在檔案的同一個位置。結論:我們需要一個函數,傳回Access資料庫中儲存為OLE物件的JPG檔案的SOI標記的位置。
正確的方法—思路四(The correct way - take four!)
提供了Blob類型欄位後,我們的函數應傳回ADOBlobStream中'FFD8'字串的位置。 ReadBuffer(讀緩衝區)從流中一個位元組一個位元組的讀取資料。對ReadBuffer的每個呼叫都會一個位元組一個位元組的移動流的位置。當兩個位元組一起引出SOI標記時,函數會傳回流的位置。這是這個函數:
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
發送; //try
end;
一旦我們有了SOI標記的位置,我們就能使用它在ADOBlob流中找到圖片的位置。
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;
運行工程,OK!
現在誰會說程式設計沒有趣味?
注意:在真正的程式碼程式中,我們會在TDataSet的AfterScroll事件中加入程式碼用於從目前行中讀取和顯示圖像(它在ADOTable1AfterScroll事件過程中)。當應用程式從一個記錄滾到另一個記錄時,AfterScroll事件發生。
思路五!
這就是本章的主要內容。現在你可以儲存和顯示所有你感興趣的JPG圖片。在這篇文章的最後一頁,我會提供完整的程式碼(form1單元);所有的資料安排都放在表單的OnCreate事件中。這確保了所有的三個元件被正確連接—在設計時你不需要使用Object Inspector(物件檢視器)。
我承認,這一章不適合初學者,但世界是殘酷的!另一件事:你注意到最後你都不知道怎麼改變(或增加一些新的)表中的圖片!是的,那又是另一個完整的故事了!