对于 Oracle 用户来说,存在一个很大的问题。
您必须使用 PDO::PARAM_STR 而不是 PDO::PARAM_LOB 来保存 CLOB 对象。
但您必须发送第 4 个参数,通常是 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 对象。
但您必须发送第 4 个参数,通常是 strlen($subject),否则您会收到 LONG 错误。
示例 1 中似乎存在影响它的一个错误。PDO::PARAM_LOB 在与 pdo::bindColumn() 一起使用时应该返回一个流,但它返回一个字符串。将此字符串传递给 fpassthru() 然后会触发一个错误,错误消息为“提供的参数不是有效的流资源”。这已在错误报告 #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() 具有相同的效果。
PDOStatement 的方法 bindParam 和 bindValue 也适用于字符串,如
<?php
$data = file_get_contents($filename);
$stmt->bindValue(1, $data, PDO::PARAM_LOB);
//...
?>
这是我在 PostgreSQL 中使它正常工作所使用的唯一方法。
我花了大量时间试图让它工作,但无论我做什么,PDO 都会损坏我的数据。
我最终发现我一直使用的是
$pdo->exec('SET CHARACTER SET utf8');
在我的连接脚本的 TRY 部分中。
当然,当您使用参数 lob 将二进制输入提供给 PDO 时,这不起作用。
对于从 Postgres 中选择数据,表中列的数据类型决定了使用 PARAM_LOB 绑定的参数返回字符串还是返回资源。
<?php
// create table 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 is a string
// create table 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 is a resource
上面列出的 DBMS 对字符字符串的最大大小有以下(默认)限制。最大值以字节为单位给出,因此如果使用多字节编码,则可存储的字符数可能更少。
CUBRID:16kB
SQL Server:2GB
Firebird:32kB
IBM Db2:32kB
Informix:32kB
MySQL:16kB
Oracle:2kB
PostgreSQL:1GB
SQLite:10 亿字节
4D:未知,但 LOB 限制为 2GB。
我发现使用 stream_get_contens 通过文件句柄获取 lob 的数据更容易。
<?php
$stmt = $pdo->con->prepare( 'select * from filetable' );
$stmt->execute();
$res = $stmt->fetchAll( PDO::FETCH_ASSOC );
for( $i=0; $i<count($res); $i++ ){
$filename = "C:/tmp/".$res[$i]['FILE_ID'].'.xml';
$content = stream_get_contents( $res[$i]['DATA_FILE'] );
file_put_contents( $filename, $content );
}
?>