第四節 在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 end; //tryend;
一旦我們有了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(對象檢視器)。
我承認,這一章不適合初學者,但世界是殘酷的!另一件事:你注意到最后你都不知道怎樣改變(或增加一些新的)表中的圖片!是的,那又是另一個完整的故事了!
新聞熱點
疑難解答
圖片精選