CascadiaPHP 2024

Objetos Grandes (LOBs)

Em algum momento da aplicação, pode ser necessário armazenar dados "grandes" em seu banco de dados. Grande geralmente significa "cerca de 4kb ou mais", embora alguns bancos de dados possam lidar facilmente com até 32kb antes que os dados se tornem "grandes". Objetos grandes podem ser de natureza textual ou binária. O PDO permite que se trabalhe com esse tipo de dado grande usando o código de tipo PDO::PARAM_LOB nas chamadas a PDOStatement::bindParam() ou PDOStatement::bindColumn(). PDO::PARAM_LOB diz ao PDO para mapear os dados como um stream, para que seja possível manipulá-los usando a API de Streams do PHP.

Exemplo #1 Exibindo uma imagem de um banco de dados

Este exemplo vincula o LOB à variável chamada $lob e depois o envia para o navegador usando fpassthru(). Como o LOB é representado como um stream, funções como fgets(), fread() e stream_get_contents() podem ser usadas nele.

<?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);
?>

Exemplo #2 Inserindo uma imagem em um banco de dados

Este exemplo abre um arquivo e passa o identificador do arquivo para o PDO para inserir como um LOB. O PDO fará o melhor possível para obter o conteúdo do arquivo até o banco de dados da maneira mais eficiente possível.

<?php
$db
= new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2');
$stmt = $db->prepare("insert into images (id, contenttype, imagedata) values (?, ?, ?)");
$id = get_new_id(); // some function to allocate a new ID

// suponha que estamos executando como parte de um formulário de upload de arquivo
// Você pode encontrar mais informações na documentação do 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();
?>

Exemplo #3 Inserindo uma imagem em um banco de dados: Oracle

O Oracle requer uma sintaxe ligeiramente diferente para inserir um lob de um arquivo. Também é essencial que você execute a inserção sob um transação, caso contrário, seu LOB recém-inserido será comprometido com um comprimento zero como parte do commit implícito que ocorre quando a consulta é executada:

<?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(); // some function to allocate a new ID

// suponha que estamos executando como parte de um formulário de upload de arquivo
// Você pode encontrar mais informações na documentação do 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();
?>

add a note

User Contributed Notes 7 notes

up
14
http://matts.org/
14 years ago
A big gotcha exists for Oracle users.

You have to save CLOB objects using PDO::PARAM_STR, not PDO::PARAM_LOB.

But you MUST send the 4th argument, usually strlen($subject) or you get a LONG error.
up
12
Jeremy Cook
14 years ago
There seems to be a bug that affects example 1 above. PDO::PARAM_LOB when used with pdo::bindColumn() is supposed to return a stream but it returns a string. Passing this string to fpassthru() then triggers an error with the message 'supplied argument is not a valid stream resource'. This has been reported in bug #40913. The work around is to do the following:

<?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);
?>

Since the browser is expecting an image after the call to header() writing the string representation of the binary output with echo() has the same affect as calling fpassthru().
up
10
diogoko at gmail dot com
15 years ago
PDOStatement's methods bindParam and bindValue also work with strings, as in:

<?php
$data
= file_get_contents($filename);
$stmt->bindValue(1, $data, PDO::PARAM_LOB);
//...
?>

This was the only way I could make it work with PostgreSQL.
up
4
knl at bitflop dot com
15 years ago
I spend a lot of time trying to get this to work, but no matter what I did PDO corrupted my data.

I finally discovered that I had been using:

$pdo->exec('SET CHARACTER SET utf8');

in the TRY part of my connection script.

This off course doesn't work when you feed binary input to PDO using the parameter lob.
up
2
ben dot leiting at gmail dot com
8 years ago
For selecting data out of Postgres, the data type of the column in the table determined if the parameter bound with PARAM_LOB returned a string or returned a resource.

<?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
up
-1
phpcoder at gmail dot com
5 years ago
The DBMSs that are listed above have these (default) limits on the maximum size of a char string. The maximum is given in bytes so the number of characters storable can be smaller if a multibyte encoding is used.

CUBRID: 16kB
SQL Server: 2GB
Firebird: 32kB
IBM Db2: 32kB
Informix: 32kB
MySQL: 16kB
Oracle: 2kB
PostgreSQL: 1GB
SQLite: 1 billion bytes
4D: Unknown, but LOBs are limited to 2GB.
up
-15
xorinox at gmx dot ch
13 years ago
I find it easier to use stream_get_contens to fetch the data of a lob using the file handle.

<?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 );
}
?>
To Top