对于 Oracle 用户来说,存在一个很大的陷阱。
您必须使用 PDO::PARAM_STR 而不是 PDO::PARAM_LOB 来保存 CLOB 对象。
但是您必须发送第四个参数,通常是 strlen($subject),否则会出现一个 LONG 错误。
在应用程序的某个时刻,您可能会发现需要在数据库中存储“大型”数据。大型通常意味着“大约 4kb 或更多”,尽管某些数据库在数据变为“大型”之前可以轻松处理高达 32kb 的数据。大型对象可以是文本的或二进制的。PDO 允许您通过在 PDOStatement::bindParam() 或 PDOStatement::bindColumn() 调用中使用PDO::PARAM_LOB
类型代码来处理这种大型数据类型。PDO::PARAM_LOB
告诉 PDO 将数据映射为流,以便您可以使用 PHP 流 API 来操作它。
示例 #1 从数据库显示图像
此示例将 LOB 绑定到名为 $lob 的变量中,然后使用 fpassthru() 将其发送到浏览器。由于 LOB 表示为流,因此可以在其上使用诸如 fgets()、fread() 和 stream_get_contents() 之类的函数。
<?php
$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2');
$stmt = $db->prepare("select contenttype, imagedata from images where id=?");
$stmt->execute(array($_GET['id']));
$stmt->bindColumn(1, $type, PDO::PARAM_STR, 256);
$stmt->bindColumn(2, $lob, PDO::PARAM_LOB);
$stmt->fetch(PDO::FETCH_BOUND);
header("Content-Type: $type");
fpassthru($lob);
?>
示例 #2 将图像插入数据库
此示例打开一个文件并将文件句柄传递给 PDO 以将其作为 LOB 插入。PDO 将尽最大努力以最有效的方式将文件内容获取到数据库中。
<?php
$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2');
$stmt = $db->prepare("insert into images (id, contenttype, imagedata) values (?, ?, ?)");
$id = get_new_id(); // 用于分配新 ID 的某个函数
// 假设我们正在文件上传表单的一部分中运行
// 您可以在 PHP 文档中找到更多信息
$fp = fopen($_FILES['file']['tmp_name'], 'rb');
$stmt->bindParam(1, $id);
$stmt->bindParam(2, $_FILES['file']['type']);
$stmt->bindParam(3, $fp, PDO::PARAM_LOB);
$db->beginTransaction();
$stmt->execute();
$db->commit();
?>
示例 #3 将图像插入数据库:Oracle
Oracle 需要稍微不同的语法才能从文件插入 lob。同样重要的是,您必须在事务下执行插入操作,否则您新插入的 LOB 将以零长度提交,作为查询执行时发生的隐式提交的一部分。
<?php
$db = new PDO('oci:', 'scott', 'tiger');
$stmt = $db->prepare("insert into images (id, contenttype, imagedata) " .
"VALUES (?, ?, EMPTY_BLOB()) RETURNING imagedata INTO ?");
$id = get_new_id(); // 用于分配新 ID 的某个函数
// 假设我们正在文件上传表单的一部分中运行
// 您可以在 PHP 文档中找到更多信息
$fp = fopen($_FILES['file']['tmp_name'], 'rb');
$stmt->bindParam(1, $id);
$stmt->bindParam(2, $_FILES['file']['type']);
$stmt->bindParam(3, $fp, PDO::PARAM_LOB);
$db->beginTransaction();
$stmt->execute();
$db->commit();
?>
对于 Oracle 用户来说,存在一个很大的陷阱。
您必须使用 PDO::PARAM_STR 而不是 PDO::PARAM_LOB 来保存 CLOB 对象。
但是您必须发送第四个参数,通常是 strlen($subject),否则会出现一个 LONG 错误。
PDOStatement 的方法 bindParam 和 bindValue 也适用于字符串,例如
<?php
$data = file_get_contents($filename);
$stmt->bindValue(1, $data, PDO::PARAM_LOB);
//...
?>
这是我让它在 PostgreSQL 中工作的唯一方法。
上面示例 1 似乎存在一个影响它的 bug。PDO::PARAM_LOB 与 pdo::bindColumn() 一起使用时,应该返回一个流,但它返回一个字符串。将此字符串传递给 fpassthru() 然后会触发错误,消息为“提供的参数不是有效的流资源”。这已在 bug #40913 中报告。解决方法如下
<?php
$stmt = $db->prepare("select contenttype, imagedata from images where id=?");
$stmt->execute(array($_GET['id']));
$stmt->bindColumn(1, $type, PDO::PARAM_STR, 256);
$stmt->bindColumn(2, $lob, PDO::PARAM_LOB);
$stmt->fetch(PDO::FETCH_BOUND);
header("Content-Type: $type");
echo($lob);
?>
由于浏览器在调用 header() 后期望一个图像,因此使用 echo() 写入二进制输出的字符串表示与调用 fpassthru() 具有相同的效果。
我花了很长时间试图让它工作,但无论我做了什么,PDO 都损坏了我的数据。
我最终发现我一直在使用
$pdo->exec('SET CHARACTER SET utf8');
在我的连接脚本的 TRY 部分。
当您使用参数 lob 向 PDO 提供二进制输入时,这当然不起作用。
对于从 Postgres 中选择数据,表中列的数据类型决定了使用 PARAM_LOB 绑定的参数是返回字符串还是返回资源。
<?php
// 创建表 log ( data text ) ;
$geth = $dbh->prepare('select data from log ');
$geth->execute();
$geth->bindColumn(1, $dataString, PDO::PARAM_LOB);
$geth->fetch(PDO::FETCH_BOUND);
echo ($dataString); // $dataString 是一个字符串
// 创建表 log ( data bytea ) ;
$geth = $dbh->prepare('select data from log');
$geth->execute();
$geth->bindColumn(1, $dataFH, PDO::PARAM_LOB);
$geth->fetch(PDO::FETCH_BOUND);
fpassthru($dataFH); // $dataFH 是一个资源
上面列出的 DBMS 对 char 字符串的最大大小有这些(默认)限制。最大值以字节为单位给出,因此如果使用多字节编码,则可存储的字符数可能更少。
CUBRID:16kB
SQL Server:2GB
Firebird:32kB
IBM Db2:32kB
Informix:32kB
MySQL:16kB
Oracle:2kB
PostgreSQL:1GB
SQLite:10 亿字节
4D:未知,但 LOB 限制为 2GB。