Oracle LOB (Large Object) 包含 BLOB (二進位)、CLOB (字元)、NCLOB (國家字元集) 與 BFILE (外部檔案)。PHP OCI8 擴展提供完整 LOB API,本文詳細說明建立、讀取、寫入、暫時 LOB、流式處理與效能優化。
| 型別 | 資料類型 | 最大容量 | 典型用途 | OCI8 常數 |
|---|---|---|---|---|
| BLOB | 二進位 | 4GB - 1 | 圖片、影音、加密檔、壓縮包 | OCI_B_BLOB |
| CLOB | 單位元組字元 | 4GB - 1 | 長文本、XML、JSON、日誌 | OCI_B_CLOB |
| NCLOB | 多位元組字元 (Unicode) | 4GB - 1 | 多語言長文本 | OCI_B_NCLOB (PHP 8.1+) |
| BFILE | 外部檔案參考 | OS 限制 | 唯讀大檔、CDN 來源 | OCI_B_FILE |
oci_new_descriptor($conn, OCI_D_LOB) 建立 LOB 描述符oci_bind_by_name($stid, ':lob', $lob, -1, OCI_B_BLOB|OCI_B_CLOB) 綁定INSERT ... RETURNING lob_col INTO :lob 或 SELECT lob_col ...)writeTemporary(), load(), read(), write(), save(), free()<?php
$conn = oci_connect('user', 'pass', '//host:1521/service', 'AL32UTF8');
$imagePath = 'photo.jpg';
$imageData = file_get_contents($imagePath);
$stid = oci_parse($conn, 'INSERT INTO images (id, mime, data) VALUES (seq.nextval, :mime, :blob) RETURNING data INTO :blob');
$mime = 'image/jpeg';
$blob = oci_new_descriptor($conn, OCI_D_LOB);
oci_bind_by_name($stid, ':mime', $mime);
oci_bind_by_name($stid, ':blob', $blob, -1, OCI_B_BLOB);
// 寫入暫時 LOB
$blob->writeTemporary($imageData, OCI_TEMP_BLOB);
$r = oci_execute($stid, OCI_NO_AUTO_COMMIT);
if ($r) {
oci_commit($conn);
echo "上傳成功\n";
} else {
$e = oci_error($stid); trigger_error($e['message'], E_USER_ERROR);
}
$blob->free();
oci_free_statement($stid);
oci_close($conn);
<?php
$conn = oci_connect('user', 'pass', '//host:1521/service', 'AL32UTF8');
$stid = oci_parse($conn, 'SELECT content FROM articles WHERE id = :id');
$id = 123;
oci_bind_by_name($stid, ':id', $id);
oci_execute($stid);
$row = oci_fetch_assoc($stid);
if ($row) {
$clob = $row['CONTENT']; // 自動為 LOB 描述符
$clob->load(); // 將資料從資料庫載入描述符
$content = $clob->read($clob->size()); // 一次性讀取 (小檔案)
$clob->free();
echo $content;
}
oci_free_statement($stid);
oci_close($conn);
// 讀取大型 BLOB/CLOB 分塊輸出 (如檔案下載)
$clob->load();
$chunkSize = 8192;
while (!$clob->eof()) {
echo $clob->read($chunkSize);
ob_flush(); flush(); // 輸出到瀏覽器
}
$clob->free();
// 寫入大型 LOB 分塊
$blob = oci_new_descriptor($conn, OCI_D_LOB);
$stid = oci_parse($conn, 'INSERT INTO files (data) VALUES (:blob) RETURNING data INTO :blob');
oci_bind_by_name($stid, ':blob', $blob, -1, OCI_B_BLOB);
$blob->writeTemporary('', OCI_TEMP_BLOB); // 初始化空暫時 LOB
$handle = fopen('large_video.mp4', 'rb');
while (!feof($handle)) {
$chunk = fread($handle, 8192);
$blob->write($chunk); // 追加寫入
}
fclose($handle);
oci_execute($stid, OCI_NO_AUTO_COMMIT);
oci_commit($conn);
$blob->free();
| 策略 | 實作 | 效益 |
|---|---|---|
| SecureFile LOB (Oracle 11g+) | 建表指定 STORE AS SECUREFILE | 壓縮、去重、快取、加密、更好並發 |
| LOB 快取 | CACHE / NOCACHE / CACHE READS 屬性 | 頻繁讀取啟用 CACHE |
| 啟用壓縮 | COMPRESS HIGH/MEDIUM/LOW | 文字型 LOB 壓縮率 60-80% |
| 去重 | DEDUPLICATE | 相同內容共享儲存 |
| 分塊大小 | CHUNK 8K/16K/32K (預設 8K) | 匹配應用讀寫模式 |
| 避免 SELECT * | 明確列出欄位,不要包含 LOB 欄位除非必要 | 減少網路傳輸、記憶體 |
| 使用暫時 LOB | OCI_TEMP_BLOB/CLOB 處理中介資料 | 不產生 UNDO/REDO,速度快 |
ORA-22275: invalid LOB locator → LOB 描述符已 free() 或交易已提交失效,重新查詢取得ORA-01555: snapshot too old → 長交易中 LOB 讀取,增加 UNDO 保留時間或縮短交易AL32UTF8,CLOB 存儲為 UTF-8,PHP 內部 UTF-8read($lob->size())OCI_Lob::save(): ORA-22920 → 嘗試對非暫時 LOB 直接 write(),需先 writeTemporary() 或 load()
// 建立目錄物件 (DBA 執行)
// CREATE DIRECTORY media_dir AS '/data/media';
// GRANT READ ON DIRECTORY media_dir TO php_user;
$stid = oci_parse($conn, 'SELECT bfile_col FROM documents WHERE id = :id');
oci_bind_by_name($stid, ':id', $id);
oci_execute($stid);
$row = oci_fetch_assoc($stid);
$bfile = $row['BFILE_COL'];
$bfile->open(OCI_FILE_READONLY);
while (!$bfile->eof()) {
echo $bfile->read(8192);
}
$bfile->close();
$bfile->free();
掌握 LOB 操作是 PHP 開發企業級 Oracle 應用的核心技能,善用 SecureFile、流式處理與暫時 LOB 可大幅提升效能與穩定性。