將多個檔案打包進資料庫單一資料表的 BLOB (Binary Large Object) 欄位,利用資料庫交易、索引、備份機制管理檔案集合,是輕量級文件管理系統的經典架構。本文示範使用 TFileStream、TBlobField.LoadFromStream/SaveToStream 實作檔案匯入/匯出,並延伸壓縮加密應用。
CREATE TABLE FilePackage (
FileName VARCHAR(255) PRIMARY KEY, -- 檔案名 (含相對路徑)
FileStream BLOB, -- 檔案二進位內容
FileSize BIGINT, -- 原始大小
CRC32 INTEGER, -- 校驗和
ModTime TIMESTAMP, -- 修改時間
Attr INTEGER -- 檔案屬性
);
Edit1: TEdit — 檔案路徑輸入BtnImport: TButton — 打包匯入BtnExport: TButton — 解包匯出ADOTable1: TADOTable — 連結 FilePackageDataSource1: TDataSource — 連結 ADOTable1procedure TForm1.BtnImportClick(Sender: TObject);
var
FS: TFileStream;
FilePath: string;
begin
FilePath := Edit1.Text;
if not FileExists(FilePath) then
raise Exception.Create('檔案不存在: ' + FilePath);
FS := TFileStream.Create(FilePath, fmOpenRead or fmShareDenyWrite);
try
ADOTable1.Append;
try
// 關鍵:TBlobField.LoadFromStream 直接將串流寫入 BLOB
TBlobField(ADOTable1.FieldByName('FileStream')).LoadFromStream(FS);
ADOTable1.FieldByName('FileName').AsString := ExtractFileName(FilePath);
ADOTable1.FieldByName('FileSize').AsLargeInt := FS.Size;
ADOTable1.FieldByName('ModTime').AsDateTime := FileDateToDateTime(FileAge(FilePath));
ADOTable1.FieldByName('Attr').AsInteger := FileGetAttr(FilePath);
// CRC32 計算略
ADOTable1.Post;
except
ADOTable1.Cancel;
raise;
end;
finally
FS.Free;
end;
ShowMessage('匯入完成: ' + ExtractFileName(FilePath));
end;
procedure TForm1.BtnExportClick(Sender: TObject);
var
MS: TMemoryStream;
FileName: string;
begin
if ADOTable1.IsEmpty then
raise Exception.Create('資料表無資料');
ADOTable1.First; // 示範匯出第一筆,實務應遍歷或定位
FileName := ADOTable1.FieldByName('FileName').AsString;
MS := TMemoryStream.Create;
try
// 關鍵:TBlobField.SaveToStream 將 BLOB 讀入串流
TBlobField(ADOTable1.FieldByName('FileStream')).SaveToStream(MS);
MS.Position := 0;
MS.SaveToFile(FileName);
finally
MS.Free;
end;
ShowMessage('匯出完成: ' + FileName);
end;
procedure TForm1.BatchImport(const Folder: string);
var
SR: TSearchRec;
FS: TFileStream;
Total, Done: Integer;
begin
if FindFirst(IncludeTrailingPathDelimiter(Folder) + '*.*', faAnyFile, SR) = 0 then
try
repeat
if (SR.Attr and faDirectory) = 0 then
Inc(Total);
until FindNext(SR) <> 0;
finally
FindClose(SR);
end;
Done := 0;
if FindFirst(IncludeTrailingPathDelimiter(Folder) + '*.*', faAnyFile, SR) = 0 then
try
repeat
if (SR.Attr and faDirectory) = 0 then
begin
FS := TFileStream.Create(Folder + '\' + SR.Name, fmOpenRead or fmShareDenyWrite);
try
ADOTable1.Append;
TBlobField(ADOTable1.FieldByName('FileStream')).LoadFromStream(FS);
ADOTable1.FieldByName('FileName').AsString := SR.Name;
ADOTable1.FieldByName('FileSize').AsLargeInt := FS.Size;
ADOTable1.Post;
Inc(Done);
// 更新進度條
ProgressBar1.Position := Done * 100 div Total;
Application.ProcessMessages;
finally
FS.Free;
end;
end;
until FindNext(SR) <> 0;
finally
FindClose(SR);
end;
end;
利用串流組合模式,在資料寫入 BLOB 前透過管道處理:
uses Zlib, IdHashMessageDigest; // 需第三方或系統單元
// 壓縮管道匯入
procedure CompressAndStore(const SourceFile: string; BlobField: TBlobField);
var
FS: TFileStream;
CS: TCompressionStream; // Zlib 壓縮串流
MS: TMemoryStream;
begin
FS := TFileStream.Create(SourceFile, fmOpenRead);
MS := TMemoryStream.Create;
try
CS := TCompressionStream.Create(clMax, MS);
try
CS.CopyFrom(FS, 0); // 檔案 → 壓縮 → 記憶體
finally
CS.Free; // 必須 Free 才會寫入 Zlib 尾部資訊
end;
MS.Position := 0;
BlobField.LoadFromStream(MS); // 記憶體 → BLOB
finally
MS.Free;
FS.Free;
end;
end;
// 解壓縮管道匯出
procedure ExtractAndDecompress(BlobField: TBlobField; const DestFile: string);
var
MS: TMemoryStream;
DS: TDecompressionStream;
FS: TFileStream;
begin
MS := TMemoryStream.Create;
try
BlobField.SaveToStream(MS); // BLOB → 記憶體
MS.Position := 0;
DS := TDecompressionStream.Create(MS);
FS := TFileStream.Create(DestFile, fmCreate);
try
FS.CopyFrom(DS, 0); // 解壓縮 → 檔案
finally
FS.Free;
DS.Free;
end;
finally
MS.Free;
end;
end;
| 因素 | 建議 |
|---|---|
| 單一檔案大小 | Access 限制 ~2GB,SQLite/Firebird/PostgreSQL 支援更大。大檔案建議分塊存儲或改用檔案系統 + 資料庫存路徑 |
| 記憶體使用 | TMemoryStream 全載入記憶體,大檔案改用 TFileStream 做中繼或分塊讀寫 |
| 交易控制 | 批次匯入建議 ADOConnection.BeginTrans / CommitTrans,失敗 RollbackTrans |
| 並發存取 | BLOB 欄位鎖定粒度較大,高並發場景建議檔案系統 + 資料庫元資料分離 |
TBlobField.LoadFromStream/SaveToStream 提供了極簡潔的檔案⇄資料庫串流橋樑。配合串流管道模式,可無縫整合壓縮、加密、校驗等横切關注點。對於中小型文件管理、離線資料同步、應用程式內嵌資源庫等場景,此方案開發效率高、部署簡單 (單一資料庫檔案)、維護容易。