In PHP, say we have the following:
$sampleImageBase64 = 'R0lGODlhGQAZAPAAAO0cJAAAACH5BAAAAAAALAAAAAAZABkAAAIXhI+py+0Po5y02ouz3rz7D4biSJamWAAAOw==';
$sampleImageBinary = base64_decode($sampleImageBase64);
$sampleImageHexadecimal = bin2hex($sampleImageBinary);
We store the binary file in the MSSQL database as follows wherein blob
column is of type VARBINARY(MAX)
and we pass in insertImage($sampleImageBinary)
. Note that both methods insertImage()
and insertImageAlternate()
work. The former instructs the PDO driver to handle the bound paramater as a SQL large object data type with the driver option of PDO::SQLSRV_ENCODING_BINARY
which is part of the PDO_SQLSRV driver. The latter treats the passed in variable as a string that is casted to binary by the SQL query - in other words, SQL converts it whereas the PDO driver handled the binary stream in the former method.
From the documentation:
PDO::SQLSRV_ENCODING_BINARY (integer)
Specifies that data is sent/retrieved as a raw byte stream to/from the server without performing encoding or translation. This constant can be passed to PDOStatement::setAttribute, PDO::prepare, PDOStatement::bindColumn, and PDOStatement::bindParam.
You can work with a stream resource using any of the stream functions https://www.php.net/manual/en/ref.stream.php, for example stream_get_contents()
https://www.php.net/manual/en/function.stream-get-contents.php.
/**
* @param $blob
*
* @return int|void
*/
public function insertImage($blob)
{
$mssql = $this->dbh->getInstance('LocalMSSQL');
$mssql->beginTransaction();
try {
$stmt = $mssql->prepare("INSERT INTO images (blob) VALUES (?)");
$stmt->bindParam(1, $blob, PDO::PARAM_LOB, 0, PDO::SQLSRV_ENCODING_BINARY);
$stmt->execute();
$mssql->commit();
return $mssql->lastInsertId();
} catch(PDOException $e) {
$mssql->rollBack();
throw new PDOException($e); // bubble exception
}
}
/**
* @param $blob
*
* @return int|void
*/
public function insertImageAlternate($blob)
{
$mssql = $this->dbh->getInstance('LocalMSSQL');
$mssql->setAttribute(PDO::SQLSRV_ATTR_ENCODING, PDO::SQLSRV_ENCODING_SYSTEM);
$mssql->beginTransaction();
try {
$stmt = $mssql->prepare("
INSERT INTO images (
blob
) VALUES (
(CAST(:blob as VARBINARY(MAX)))
)
");
$stmt->execute([$blob]);
$mssql->commit();
return $mssql->lastInsertId();
} catch(PDOException $e) {
$mssql->rollBack();
throw new PDOException($e); // bubble exception
}
}
MSSQL stores the image as binary in the respective column, but displays is as a hexadecimal value in Microsoft SQL Server Management Studio as well as returns it in hexadecimal format when retreiving the column in PHP.
/**
* @param $id
*
* @return mixed
*/
public function retrieveImage($id)
{
$stmt = $this->dbh->getInstance('LocalMSSQL')->prepare("SELECT blob FROM images WHERE id = ?");
$stmt->bindValue(1, $id, PDO::PARAM_INT);
$stmt->execute();
return $stmt->fetch(PDO::FETCH_COLUMN);
}
/**
* @param $id
*
* @return mixed
*/
public function retrieveImageAsStream($id)
{
$conn = $this->dbh->getInstance('LocalMSSQL');
$stmt = $conn->prepare("SELECT inputType, blob FROM images WHERE id = ?");
$stmt->bindValue(1, $id, PDO::PARAM_INT);
$stmt->execute();
$resultSet = [
'inputType' => null,
'blob' => null,
];
$stmt->bindColumn(1, $resultSet['inputType'], PDO::PARAM_STR);
$stmt->bindColumn(2, $resultSet['blob'], PDO::PARAM_LOB, 0, PDO::SQLSRV_ENCODING_BINARY);
if ($stmt->fetch()) {
return $resultSet;
} else {
return false;
}
}
For example, the resultset returned from the above query (which contains the base64 image from above) is: 47494638396119001900F00000ED1C2400000021F90400000000002C0000000019001900000217848FA9CBED0FA39CB4DA8BB3DEBCFB0F86E24896A65800003B
You can render this out the client in PHP in two ways:
$image = $ImageModel->retrieveImage();
header('Content-type: image/png');
echo hex2bin($image);
$image = $ImageModel->retrieveImage();
// convert hexadecimal to binary then base64 encode it
$base64 = base64_encode(hex2bin($image));
echo '<img src="data:image/jpeg;base64,' . $base64 . '">';
See https://blogs.msdn.microsoft.com/brian_swan/2010/04/20/comparing-the-sqlsrv-and-pdo-apis/ for more details.