理解 TBlobStream 必須先認識其私有欄位定義:
TBlobStream = class(TStream)
private
FField: TBlobField; { 關聯的資料庫 BLOB 欄位 }
FDataSet: TDataSet; { 所屬資料集(TTable 或 TQuery) }
FRecord: PChar; { 內部緩衝區,儲存記錄資料(不含 BLOB) }
FBuffer: PChar; { 暫存記錄資料的緩衝區指標 }
FFieldNo: Integer; { BLOB 欄位編號 }
FOpened: Boolean; { 開啟狀態旗標 }
FModified: Boolean; { 修改旗標 }
FPosition: Longint; { 目前流位置 }
public
...
end;
FField:與 BLOB 流關聯的資料庫欄位,供內部存取。FDataSet:包含該欄位的資料集元件。FRecord / FBuffer:內部緩衝區,儲存記錄的非 BLOB 資料,作為 BDE API 參數。FFieldNo:欄位編號,用於 BDE API 參數傳遞。FOpened、FModified:狀態旗標。FPosition:BLOB 流目前位置。constructor TBlobStream.Create(Field: TBlobField; Mode: TBlobStreamMode);
var
OpenMode: DbiOpenMode;
begin
FField := Field;
FDataSet := Field.DataSet;
FRecord := FDataSet.ActiveBuffer;
FFieldNo := Field.FieldNo;
if FDataSet.State = dsFilter then
DBErrorFmt(SNoFieldAccess, [FField.DisplayName]);
if not FField.FModified then
begin
if Mode = bmRead then
begin
FBuffer := AllocMem(FDataSet.RecordSize);
FRecord := FBuffer;
if not FDataSet.GetCurrentRecord(FBuffer) then Exit;
OpenMode := dbiReadOnly;
end
else
begin
if not (FDataSet.State in [dsEdit, dsInsert]) then DBError(SNotEditing);
OpenMode := dbiReadWrite;
end;
Check(DbiOpenBlob(FDataSet.Handle, FRecord, FFieldNo, OpenMode));
end;
FOpened := True;
if Mode = bmWrite then Truncate;
end;
建構子流程:
Field 初始化 FField、FDataSet、FRecord、FFieldNo。dsFilter 狀態則拋出例外。FField.FModified = False):
bmRead):配置記憶體緩衝區,複製目前記錄(不含 BLOB),設為唯讀模式。bmWrite/bmReadWrite):檢查資料集是否處於編輯/插入狀態,設為讀寫模式。DbiOpenBlob 開啟 BLOB 欄位。FOpened := True;若為 bmWrite 則呼叫 Truncate 清空現有資料。dsEdit 或 dsInsert 狀態才能操作 BLOB。TBlobStream 時不會重複呼叫 DbiOpenBlob,僅設 FOpened := True,允許多個 BLOB 流同時操作同一欄位。destructor TBlobStream.Destroy;
begin
if FOpened then
begin
if FModified then FField.FModified := True;
if not FField.FModified then
DbiFreeBlob(FDataSet.Handle, FRecord, FFieldNo);
end;
if FBuffer <> nil then FreeMem(FBuffer, FDataSet.RecordSize);
if FModified then
try
FField.DataChanged;
except
Application.HandleException(Self);
end;
end;
解構子邏輯:
FModified := True。DbiFreeBlob)。FBuffer。DataChanged 事件通知資料變更。重要觀察:BLOB 欄位修改直到 Destroy 時才真正提交,因為讀寫直接繞過 TField 呼叫 BDE API。使用 BDE API 程式設計時,務必同步更新對應資料庫元件的狀態。