Return Image from SQL database - php

I'm using an odbc connection to connect to an SQL server. I need to display all the fields below:
Name: ID
Type: tinyint
Name: GroupId Type: smallint
Name: PhotoType: image
I'm using the code below which obviously isn't working:
<?php
$serverName = "TESTSERV\SQLEXPRESS"; //serverName\instanceName
$database = "test";
$user = "sa";
$password="#r#g0nSQLS#";
$DSN_TEST="TESTSERV - TEST";
$DSN_InetDb="TESTSERV - InetDb";
$DSN_taclog="TESTSERV-TACLOG";
$DSN_general="odbc-test";
$conn_TEST = odbc_connect ($DSN_TEST, $user, $password);
$conn_InetDb = odbc_connect($DSN_InetDb, $user, $password);
$conn_taclog = odbc_connect($DSN_taclog, $user, $password);
$conn_general=odbc_connect($DSN_general, $user, $password);
//confirming connectivity
if ($conn_general){
echo ('Connected'.'<br>');
}
else{
echo ('Not connected');
}
$sql = "SELECT * FROM InetDb.dbo.IndivImages WHERE IndivNdx = 30";
$sql_run = odbc_exec($conn_general,$sql);
$row = odbc_fetch_array($sql_run,3);
echo'<img src="data:image/jpeg;base64,'.base64_encode($row['UserImage']).'">';
?>
When executing this code, images are replaced with a chunk of characters.
How would I be able to return the images instead of a random characters ?
Thanks in advance,
J

You won't end up with actual images if you just echo out binary -- or base64-encoded -- data you've stored in the database. You'll just get the raw data blurted out.
If your images are stored as binary data, you need to serve it out via a script that outputs an image header -- e.g. header('Content-Type: image/jpeg'); -- followed by echoing out the raw data. You would then link to it e.g. as <img src="imager.php?img_id=124123123..." />. You could pass the data between your script and the "imager" e.g. over a $_SESSION variable.
If your images are stored as base64-encoded data (or if you convert the raw data on the fly), you can also use <img src="data:image/jpeg;base64,foBar1Nwht3v5R..." />. Be aware though that this isn't exactly bandwidth-friendly, and is better reserved for icons etc. rather than large image files.
Third option is to simply write the image data to the filesystem and then link to it with a standard img tag. That would make your images more easily cacheable, too. (There are ways around to ensure caching with the above options too, but writing them out, even into a /tmp directory if they are not reused, may be a simpler approach, and also save you some database performance.)

Related

Showing (pdf) content of LONGBLOB in PHP

I am trying to show the PDF file that is stored in my LONGBLOB. When I execute the code I only get the name of the file. I want to show the PDF file in a viewer.
Does someone know how I can fix this?
Here is my script:
<?php
$tpurchase_id = $_GET['tpurchase_id'];
$conn = new mysqli("localhost","user","","db");
$sql = "SELECT * FROM temp_purchase WHERE tpurchase_id= '$tpurchase_id'";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
header("Content-type:application/pdf");
$a=$row['content'];
echo '<object data="data:application/pdf;base64,';
echo base64_decode($a);
echo '" type="application/pdf" style="height:200px;width:60%"></object>';
}
}
$conn->close();
?>
I think this sould work without problem , giving more headers informations
header("Content-type: application/pdf");
header('Content-disposition: attachment; filename="thing.pdf"');
header("Content-Length: " . strlen($row['content']));
print $row['content'];
Some order of operations improvements and optimizations.
Assuming the database is correctly storing the entire contents of the LOB data.
Output buffering using ob_start, ob_clean, and ob_end_flush will provide more control over the desired response content from your script. This will help to alleviate erroneous spaces or emitted warnings from being included in the binary output.
Additionally this allows for you to control which header data to send in the response.
There's no need to use while($row = $result->fetch_assoc()) since the response from the database should contain the entirety of the single LONGBLOB data row.
Using mysqli_stmt::bind_result and mysqli_stmt::fetch will reduce some overhead caused by fetching into an associative array, since its only needed to retrieve the content. mysqli_stmt::fetch will return NULL if there are no results/data or false on error.
I also suggest using prepared statements, to prevent SQL injections and filter_var to ensure the user supplied input is of the expected data type and is valid.
Using content-disposition: inline will ask the browser to attempt to load it, if the browser can understand the content-type, otherwise it will download it.
Lastly you do not need to end your code with ?>, which can cause unintended behavior, instead just use exit;. It's best to exclude the closing tag in your PHP script files unless you are transitioning from PHP to plain text or markup in the same file.
I tested the below against my MySQL database table that also uses LONGBLOB to store PDF files and is functioning as desired.
<?php /*line 1*/
ob_start(); //start output buffering immediately
$conn = new mysqli('localhost','user','','db');
if (mysqli_connect_errno()) {
exit;
}
$tpurchase_id = filter_var($_GET['tpurchase_id'], FILTER_VALIDATE_INT);
$stmt = $conn->prepare('SELECT tp.content
FROM temp_purchase AS tp
WHERE tp.tpurchase_id = ?
AND tp.content > ""'); //ensure content is not empty
if ($stmt && false !== $tpurchase_id) {
$stmt->bind_param('i', $tpurchase_id);
$stmt->execute();
$stmt->bind_result($content);
if ($stmt->fetch()) {
//a record was found, change to a PDF file
ob_clean(); //clear the buffer
header('content-type: application/pdf');
header('content-disposition: inline; filename="Test.pdf"');
echo $content;
ob_end_flush(); //output only the buffered content to the client
}
$stmt->close();
unset($content, $stmt);
}
$conn->close(); //always close the connection
while (ob_get_level() > 0) {
ob_end_clean(); //remove everything else from the buffer
}
exit;
This will result in only the headers and content response being sent to the client, otherwise if a result from the database is not found, a blank plain/text response is sent.
The above script can then be used as the source for your inline object.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
</head>
<body>
<object data="/path/to/above_script.php?tpurchase_id=123" type="application/pdf" style="height:200px;width:60%"></object>
</body>
</html>
Aside from the above, there other points that can cause issues, which we are currently not aware of.
Headers added or modified by the webserver (apache, nginx, IIS, etc).
Upload form or PHP processing script modifying or not sending complete LOB data to the database.
Database truncating or altering the LOB data.
To use the above PHP script for displaying inline object(s). There's no need for output buffering. However you would need to swap out base64_decode in favor of using base64_encode. Decode takes a base64 encoded string and converts it to the original format. Where you actually want to take the binary data from the database and convert it to a base64 encoded string for the browser to later decode. If the file content has already been base64_encode'd by the upload processing script, neither base64_encode or base64_decode is needed.
Tested the below and is functioning as desired.
<?php /*line 1*/
$conn = new mysqli('localhost','user','','db');
if (mysqli_connect_errno()) {
exit;
}
$tpurchase_id = filter_var($_GET['tpurchase_id'], FILTER_VALIDATE_INT);
$stmt = $conn->prepare('SELECT tp.content
FROM temp_purchase AS tp
WHERE tp.tpurchase_id = ?
AND tp.content > ""'); //ensure content is not empty
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
</head>
<body>
<?php
if ($stmt && false !== $tpurchase_id) {
$stmt->bind_param('i', $tpurchase_id);
$stmt->execute();
$stmt->bind_result($content);
if ($stmt->fetch()) { ?>
<object data="data:application/pdf;base64,<?php echo base64_encode($content); ?>" type="application/pdf" style="height:200px;width:60%"></object>
<?php }
$stmt->close();
unset($content, $stmt);
}
$conn->close();
?>
</body>
</html>
For the purposes of retrieving multiple documents, you can optionally change if ($stmt->fetch()) in favor of using while($stmt->fetch())
Upload processor Suggestion
Assuming you're using the code in your question "Auto submit is not posting data to database" for the file uploading, I highly recommend that you rewrite the upload processor using current standards/best-practices, which will also make your upload processor compatible with this answer.
Using addslashes or other escaping techniques can cause issues with the resulting LOB data stored in the database. Which I surmise is the cause for the complications you are experiencing now.
You should also take into consideration the max packet size that is used by your PHP and database environment, that limits the data size that can be sent or received by your application, which can lead to truncated LOB data. Because of the packet size limits, it is recommended that you use send_long_data to prevent issues with your application transmitting the LOB data.
upload-form.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
</head>
<body>
<form id="target" method="post" enctype="multipart/form-data" name="frmImage" class="frmImageUpload" action="./post.php">
<input type="file" name="userfile" id="userfile" class="userfile"/>
</form>
</body>
</html>
post.php
<?php
$conn = new mysqli('localhost','user','','db');
if (mysqli_connect_errno()) {
exit;
}
if (!session_id()) {
session_start();
}
//never trust data from GLOBALS
$user_id = filter_var($_SESSION['user_id'], FILTER_VALIDATE_INT);
if (false === $user_id ||
!isset($_FILES) ||
!array_key_exists('userfile', $_FILES) ||
UPLOAD_ERR_OK !== $_FILES['userfile']['error'] ||
$_FILES['userfile']['size'] <= 0 ||
!is_uploaded_file($_FILES['userfile']['tmp_name'])
) {
//invalid user or file upload
exit;
}
//params = { 0: user_id, 1: content }
$stmt = $conn->prepare('INSERT INTO temp (user_id, content) VALUES (?, ?)');
if ($stmt) {
//bind default value as NULL
$null = null;
$stmt->bind_param('ib', $user_id, $null);
//max packet size limits can lead to partial file data being inserted
$fp = new SplFileObject($_FILES['userfile']['tmp_name'], 'rb', false);
while (!$fp->eof()) {
//use send_long_data to send the file data in chunks
//be sure the first argument matches the param index for the LOB data column
$stmt->send_long_data(1, $fp->fread(2048));
}
unset($fp);
$stmt->execute();
$stmt->close();
}
$conn->close();
As a personal recommendation; over the years I have come to discover that storing LOB data within the database has caused some serious issues. While it does increase the portability and ease of file management within applications. It has greatly hindered data recovery and backup, by significantly increasing the amount of I/O time needed to recover the database and hard drive RAID integrity. Also when used with other data, significantly increases query and maintenance times with the database. Forcing us to migrate from SELECT * to explicitly avoid the LOB column data or skip the tables for optimization or re-indexing. Lastly it also prevented client-side caching without creating specific RESTful URLs to serve the files. Overall it became much more trouble than was worth the effort to store the LOB data. I suggest using your web server to store the physical file(s), and use the database to store the relative path to the physical file, where PHP manages the absolute paths to the physical file(s) for viewing/manipulation. For example when creating thumbnails that can be cached and served from a publicly accessible location.
If you want to use the browser PDF viewer, note you can only view one PDF at a time.
Your code would look like:
<?php
header("Content-type:application/pdf");
$tpurchase_id = $_GET['tpurchase_id'];
$conn = new mysqli("localhost","user","","db");
$sql = "SELECT * FROM temp_purchase WHERE tpurchase_id= '$tpurchase_id'";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc())
ob_clean();
flush();
echo $row['content'];
$conn->close();
exit();
}
}
?>
Here is a solution: It gets the encoded LONGBLOB and decodes it, then displays it after setting a header. Its based on the answer from fyrye.
<?php
$tpurchase_id = $_GET['tpurchase_id'];
$connection = new mysqli("localhost","user","","db");
$sql = "SELECT content FROM temp_purchase WHERE tpurchase_id = ?";
$statement = $connection->prepare($sql);
$statement->bind_param("s", $tpurchase_id);
$statement->execute();
$statement->bind_result($pdf_encoded);
if($statement->fetch()){
$pdf_decoded = base64_decode($pdf_encoded);
}
$statement->close();
ob_start();
ob_clean(); //Clear the buffer
header('content-type: application/pdf');
echo($pdf_decoded);
ob_end_flush(); //Output only the buffered content to the client
?>

Display an image that is stored in a directory in browser

This seems like such a simple thing to do but for some reason, I can't get this to work.
I have a database with vehicle data stored in it. (Used Cars (I'm developing a car dealership website)).
I successfully display results from the database without images.
The images for each record aren't stored in the database, instead they're dumped on the server in a directory and those images are only referenced in the database.
If I echo the image name out it works fine, and if I echo the actual image out, the path is correct if you look at the image info. But in the info it states that the image is of text. i don't know how to change this.
Please find some of the code below.
<?php
$dbName = "F:/Domains/autodeal/autodeal.co.za/wwwroot/newsite/db/savvyautoweb.mdb";
// Throws an error if the database cannot be found
if (!file_exists($dbName)) {
die("Could not find database file.");
}
// Connects to the database
// Assumes there is no username or password
$conn = odbc_connect("Driver={Microsoft Access Driver (*.mdb)};Dbq=$dbName", '', '');
//this is selecting individual records
$selected_id = intval($_GET['Id']);
//this is the query
$sql = "SELECT Id, Make, Model, Year, Price, SpecialPrice, Branch, StockNO, MainPic FROM Vehicle WHERE Id = $selected_id";
// Runs the query above in the table
$rs = odbc_exec($conn, $sql);
$id = odbc_result($rs, Id);
$make = odbc_result($rs, Make);
$model = odbc_result($rs, Model);
$mainPic = odbc_result($rs, MainPic);
//this is a failsafe for when there are no images for a specific record
$no_image = "<a href='db/Nopic.gif' data-lightbox='nopic' title='No Image Available'><img src='db/Nopic.gif' /></a>";
//This successfully displays the name of the image referenced in the database
$main_pic = "<img src='db/vehicleImages/'" .$mainPic. "/>";
//this is supposed to display the actual
echo $main_pic . "<br />";
echo $no_image;
odbc_free_result($rs);
odbc_close($conn);
// This message is displayed if the query has an error in it
if (!$rs) {
exit("There is an error in the SQL!");
}
?>
Any help in the regard would be greatly appreciated.
Thank you. :)
you should use quotations around the attributes of html objects (it looks like you might be breaking things via your method):
yours:
<a href=db/Nopic.gif data-lightbox=nopic title=No Image Available><img src=db/Nopic.gif /></a>
correct:
<a href='db/Nopic.gif' data-lightbox='nopic' title='No Image Available'><img src='db/Nopic.gif' /></a>
whether or not this fixes your problem will be determined by what you come back with :p
User forward slash in front of your image paths. This way you can be sure the image path always starts from the root folder. The image path is relative to the location of the executed php script.
For example if your script.php file is in the folder /root/some_folder/script.php and you use the image path db/vehicleImages/image.jpg the script is starting the path from the same folder where it is itself which results in a path like this /root/some_folder/db/vehicleImages/image.jpg.
If you use forward slash in front of the path like this /db/vehicleImages/image.jpg it tells the script to start from the root folder like so /root/db/vehicleImages/image.jpg.
I think this is the case with your script - you are giving it the wrong path which results in a file not found.

Retrieve BLOB .bin image to be viewed

I have two files;
image.php
<?php
// include db connect class
require_once __DIR__ . '/db_connect.php';
// connecting to db
$db = new DB_CONNECT();
$result = mysql_query("SELECT *FROM products") or die(mysql_error());
//Your code to call database for image id (from _GET['id']), for instance:
$image_id = $_GET['pid'];
$data = mysql_query("Select img where pid = $image_id");
while($data = mysql_fetch_assoc($result)){
$image=$data['img'];
header("content-type: image/png");
echo $image;
}
?>
and ay.html to view the image;
<img src="image.php?pid=IMAGE_ID" />
After many examples I tried, and every thread on this site and elsewhere, I'm still getting this error;
The image"http....../image.php?pid=IMAGE_ID" cannot be displayed because it contains errors.
Any help would be appreciated.
By the way the MySql variable is a LONGBLOB called img
I wonder why there isn't a simple IMG type for MySQL variable!
You should post how you stored the image to see exactly what it's wrong with your code but as the error says the image has some kind of error hence it was not stored correctly to database.
Generally the steps to go from an image to binary data and reverse are as below:
Image to binary conversion:
<? php
$image_data=file_get_contents('test.jpg');
$encoded_image=base64_encode($image_data);
echo $encoded_image;
?>
Binary to image conversion:
<?php
$image_data = file_get_contents('test.jpg');
$encoded_image = base64_encode($image_data);
$decoded_image = base64_decode($encoded_image);
echo $decoded_image;
?>
What you're missing altogether is the base_64 encode to prevent symbols from being incorrectly ttransformed.
/In your html page.../
/* */

using _GET url link to delete a record from mysql database

EDIT
Thanks for the help so far. I have edited my post to reflect the changes suggested below. I am using PDO for my database connection. The code I have now is as follows:
HTML
<a href="includes/delete-customer.php?userID='.$row->customer_id.'">
PHP
<?php
//MySQL Database Connect
include 'includes/config.php';
// confirm that the 'id' variable has been set
if (isset($_GET['userID']) && is_numeric($_GET['userID']))
{
// get the 'id' variable from the URL
$id = $_GET['userID'];
/* Delete row from the customer table */
$id = $dbh->exec("DELETE FROM customer WHERE customer_id = '$id'");
$stmt->execute();
}
?>
config.php
<?php
/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'user';
/*** mysql password ***/
$password = 'password';
try {
$dbh = new PDO("mysql:host=$hostname;dbname=testDB", $username, $password);
}
catch(PDOException $e)
{
echo $e->getMessage();
}
?>
I'm pretty sure the HTML is correct now and the issue lies with the delete-customer.php file. I am currently receiving the following error: Fatal error: Call to a member function exec() on a non-object
I'm not sure of how to implement the PDO query correctly. Any further advice is much appreciated.
Your HTML section says:
<a href="includes/delete-customer.php?customer_id=$id['.$row->customer_id.']">
Is this your exact HTML syntax? This argument should be the actual numerical id, i.e. --
<a href="includes/delete-customer.php?customer_id=3">
-- either by echoing $row->customer_id (assuming it exists), or some other method of knowing that user id.
Your HTML only needs to send the actual data, not any sort of variable syntax. Your receiving PHP ($_GET['customer_id']) will interpret that for you and properly pass that to MySQL.
Your URL passes userID as the get parameter, yet in your php script you're trying to access customer_id. Try changing your code to retrieve userID and it should work
if (isset($_GET['userID']) && is_numeric($_GET['userID']))
<a href="includes/delete-customer.php?customer_id=<?php echo $id[$row->customer_id]; ?>">
assuming $id[$row->customer_id] is valid.
Plus, you really shouldn't delete from database on get var unless you're doing some admin validation / access rules and guarantee you don't have anyone on the job who will go rogue and manually type in numbers there.. That's just plain crazy.

how to insert a image into Db as blob in ZendFramework

I want to insert a image in blob format to db.
how to write a program in zend framework.
a simple form
just choose one image and insert into db.
In your Zend form create a file element:
$element = new Zend_Form_Element_File('fileElement');
Then you can insert the uploaded image to your DB in BLOB format like this:
$conn = new PDO("mysql:host='host';dbname='database'", 'userName', 'password');
$imagePath = $zendForm->fileElement->getFileName();
$image = file_get_contents('$imagePath');
$sql = "INSERT INTO images (data) values(?)";
$q = $conn->prepare($sql);
$q->bindParam(1, $image, PDO::PARAM_LOB);
$q->execute();
Read it in as a string using file_get_contents and store that.
That said, it is seldom a good idea to store the actual image data in the database. It is better practice to generate a unique filename and store that in the DB instead.
Here is some code to save the file in zend:
// This is to save in saveAction()
$source = $this->view->form->upload->getFileName();
$fp = fopen($source,'r');
$content = fread($fp,filesize($source));
// I don't use addslashes, because i already have that in my mapper
fclose($fp);
$model->image = base64_encode($content);
// Save here
// This is to read in imagesAction()
$this->_helper->Layout()->disableLayout();
$this->_helper->ViewRenderer->setNeverRender();
header('Content-Type: image/gif');
echo base64_decode($image);
exit;
I had alot of problems with it, try to make your save work and don't insert it into the database directly to test the reading. This won't make it possible unless you know the correct db encoding (which i don't).

Categories