首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

B/S 結構中 PHP 訪問 Oracle LOB:大對象存儲與讀取完整實戰

     編輯:PHP 綜合知識

業務場景

B/S 系統需存儲圖片、音視頻、超長文本(如公文附件、圖表)。傳統 VARCHAR2 限長 4000 字節,CLOB/BLOB 可存 4GB,是唯一選擇。

Oracle LOB 類型體系

PHP OCI8 核心函數

類型全稱用途事務支持
BLOBBinary Large Object圖片、視頻、音頻、文檔是
CLOBCharacter Large Object單字節字符長文本是
NCLOBNational Character LOB多字節字符長文本是
BFILEBinary File外部文件指針(只讀)否

環境變量配置 (Linux/Unix)

ORACLE_HOME=/u01/app/oracle
ORACLE_SID=ORCL
LD_LIBRARY_PATH=$ORACLE_HOME/lib
NLS_LANG=AMERICAN_AMERICA.ZHS16GBK
ORA_NLS33=$ORACLE_HOME/ocommon/nls/admin/data

# 驗證:分別在 oracle 用戶與 nobody/apache 用戶下執行 env 對比

Web 服務器用戶 (nobody/www) 必須加入 `oinstall`/`dba` 組。

上傳文件存入 BLOB

<?php
$conn = OCILogon('user', 'pass', 'ORCL');

// 1. 初始化 LOB 描述符
$lob = OCINewDescriptor($conn, OCI_D_LOB);

// 2. SQL: EMPTY_BLOB() 初始化 + RETURNING INTO 綁定
$stmt = OCIParse($conn, 
    "UPDATE t_file 
     SET filename = :fname, filetype = :ftype, files = EMPTY_BLOB() 
     WHERE fileid = :fid 
     RETURNING files INTO :lob"
);

// 3. 綁定參數
OCIBindByName($stmt, ':fname', $_FILES['fj1']['name']);
OCIBindByName($stmt, ':ftype', $_FILES['fj1']['type']);
OCIBindByName($stmt, ':fid', $newid);
OCIBindByName($stmt, ':lob', $lob, -1, OCI_B_BLOB); // -1 表示最大長度

// 4. 執行並寫入文件
OCIExecute($stmt, OCI_DEFAULT);
if ($lob->savefile($_FILES['fj1']['tmp_name'])) {
    OCICommit($conn);
    echo '上傳成功';
} else {
    echo '上傳失敗';
}

OCIFreeDesc($lob);
OCIFreeStatement($stmt);
?>

從 BLOB 下載文件

<?php
$conn = OCILogon('user', 'pass', 'ORCL');
$stmt = OCIParse($conn, "SELECT * FROM t_file WHERE fileid = :id");
OCIBindByName($stmt, ':id', $id);
OCIExecute($stmt);

if (OCIFetchInto($stmt, $row, OCI_ASSOC + OCI_RETURN_LOBS)) {
    // 關鍵:設置 HTTP 頭,觸發瀏覽器下載/打開
    header('Content-Type: ' . $row['FILETYPE']);
    header('Content-Disposition: attachment; filename="' . $row['FILENAME'] . '"');
    
    // 輸出二進制流
    echo $row['FILES']->load(); // 或 $row['FILES']->read($size)
}

OCIFreeStatement($stmt);
?>

關鍵最佳實踐

  1. 大文件分塊讀寫:`$lob->write($chunk)` / `$lob->read($size)` 避免內存溢出
  2. 臨時 LOB:`OCINewDescriptor($conn, OCI_D_TEMP_LOB)` 用於中間處理,會話結束自動清理
  3. 流式輸出:下載大文件時分塊 `echo` + `flush()`,降低 PHP 內存佔用
  4. 錯誤處理:檢查 `OCIError()`,記錄 Oracle 錯誤碼
  5. 權限最小化:數據庫用戶僅授予必要表的 DML 權限

性能對比結論

作者實測:Linux + Apache + PHP-OCI8 響應速度優於 Windows + OAS (JDBC)。PHP 輕量進程模型 + OCI 直接調用,減少中間層開銷。

現代化遷移建議

  • 新項目使用 **PDO_OCI** 或 **oci8** (PHP 7/8 仍維護)
  • PDO 示例:`$stmt->bindParam(':lob', $lob, PDO::PARAM_LOB)`
  • 考慮對象存儲 (S3/MinIO/OSS) + 數據庫存 URL,分離靜態資源與業務數據
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
函數作用
OCINewDescriptor($conn, $type)創建 LOB/FILE 描述符,返回句柄
OCIBindByName($stmt, $ph, &$var, $len, $type)綁定 PHP 變量到 Oracle 佔位符
OCIFetchInto($stmt, &$arr, $mode)取行,`OCI_ASSOC+OCI_RETURN_LOBS` 返回 LOB 對象
$lob->savefile($path)將文件內容寫入 LOB
$lob->load()讀取 LOB 內容為字符串