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
?>
Related
This is the image data retrieved from the database.
I want this to be the file name which is downloadable when clicked:
Here is my code for uploading the image:
if(isset($_POST['submit'])) {
include 'db1.php';
if(count($_FILES) > 0) {
if(is_uploaded_file($_FILES['userImage']['tmp_name'])) {
$docFile =addslashes(file_get_contents($_FILES['userImage']['tmp_name']));
$filetype = getimageSize($_FILES['userImage']['tmp_name']);
$docCode = $_POST['docCode'];
$docType = $_POST['docType'];
$subject = $_POST['subject'];
$comment = $_POST['comment'];
$dateWrit = $_POST['dateWrit'];
$signatory = $_POST['signatory'];
$sql = "INSERT INTO document (docCode, docType, docFile,fileType, subject, dateWrit, signatory,comment, status,staffid)
VALUES ('$docCode', '$docType', '{$docFile}','{$filetype['mime']}', '$subject', '$dateWrit', '$signatory','$comment', 'Unsent', '".$_SESSION['staffid']."')";
if ($conn->query($sql) === TRUE) {
echo "<script type='text/javascript'>alert('Succesfully added!')</script>";
} else {
echo "Error: " . $sql . "<br>" . $conn->error;
}
$conn->close();
and I called the data from the database:
echo "<tr><td>".$row['docCode']."</td>
<td>".$row['docType']."</td>
<td>".$row['subject']."</td>
<td>".$row['dateWrit']."</td>
<td>".$row['signatory']."</td>
<td> <a href=".$row['docFile']." download>".$row['docFile']."</a></td>
<td>".$row['comment']."</td>
<td>".$row['status']."</td>";
You can't dump a raw image into an href attribute and turn it into a download. The href is supposed to store a URL, not raw data.
You're going to have to set up a handler page to echo out the image data and apply appropriate headers. For example, set up a page that retrieves the image data in $row, then do something like this:
<?php
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"" . basename($file_url) . "\"");
// TODO: get the image here, based on some $_GET or $_POST parameter, and
// load it into $row
echo $row['docFile'];
(The above code is not meant to be exact, as the code in your question is pretty unclear regarding how you set $row and where, if anywhere, you save the image's name.)
Also, very important: you are wide open to SQL injection. Please read about it and use prepared statements to avoid having someone hack or hose your database.
Finally, be aware that many people consider it best practice note to store binary blobs (like images) in the database, but only a path to the file on the system. Passing binary data back and forth causes lots of problems, not least of which is bloating your database and putting a lot of load on it whenever you want to read an image.
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.)
I have all my files stored in a mysql database as blobs. I am trying to add a speed limit to the rate at which a user can download them through our PHP website. I have tried to use the "sleep(1);" method, it does not seem to work or i am not doing it right. So if anyone knows a way to limit the speed, i would love your help.
Here is my download code
$query=mysql_query("SELECT * FROM file_servers WHERE id='$file_server_id'");
$fetch=mysql_fetch_assoc($query);
$file_server_ip=$fetch['ip'];
$file_server_port=$fetch['port'];
$file_server_username=$fetch['username'];
$file_server_password=$fetch['password'];
$file_server_db=$fetch['database_name'];
$connectto=$file_server_ip.":".$file_server_port;
if (!$linkid = #mysql_connect($connectto, $file_server_username, $file_server_password, true))
{
die("Unable to connect to storage server!");
}
if (!mysql_select_db($file_server_db, $linkid))
{
die("Unable to connect to storage database!");
}
$nodelist = array();
// Pull the list of file inodes
$SQL = "SELECT id FROM file_data WHERE file_id='$file_id' order by id";
if (!$RES = mysql_query($SQL, $linkid))
{
die("Failure to retrive list of file inodes");
}
while ($CUR = mysql_fetch_object($RES))
{
$nodelist[] = $CUR->id;
}
// Send down the header to the client
header("Content-Type: $data_type");
header("Content-Length: $size");
header("Content-Disposition: attachment; filename=$name");
// Loop thru and stream the nodes 1 by 1
for ($Z = 0 ; $Z < count($nodelist) ; $Z++)
{
$SQL = "select file_data from file_data where id = " . $nodelist[$Z];
if (!$RESX = mysql_query($SQL, $linkid))
{
die("Failure to retrive file node data");
}
$DataObj = mysql_fetch_object($RESX);
echo $DataObj->file_data;
}
One way of doing this may be the combination of flush and sleep:
read part of what you get from database
output some bytes
flush the output to the user
sleep for 1 second
But also take a loot at throttle function:
http://php.net/manual/en/function.http-throttle.php
It also have an example there. I think it is better suited.
it is in the very last echo line in your code where you would like to implement throtling. Im not familiar with whether php supports throtling output.
if not, you can try to split up that content ($DataObj->file_data) you wish to echo, and echo it little piece by little piece with small pauses in between
and be sure to disable outbut buffering. otherwise all that you echo will not be outputted until the entire script is done.
So I've made this upload script and to make it more secure, I'm finding out the type of each file.
However, for some reason, the filetype is being echoed back to me!
For example:
image/jpeg; charset=binary Please upload only SWF files!
The echoed string looks same when the upload is successful.
The code:
<?php session_start();
defined('IN_SCRIPT') ? NULL : define('IN_SCRIPT', NULL);
require_once 'inc/db_connect.php';
require_once 'styles/import.php';
$style = new style_class(NULL);
if(!isset($_FILES['file']['tmp_name']) || empty($_FILES['file']['tmp_name'])) die($style->upload_no_parameter());
$filetype = system('file -bi '.$_FILES['file']['tmp_name']);
$filetype = explode(';', $filetype, 1);
if ($filetype[0] != 'application/x-shockwave-flash; charset=binary') die($style->upload_wrong_format());
$sha256 = hash_file("sha256", $_FILES['file']['tmp_name']);
$query = $db->prepare('SELECT id FROM swf WHERE hash = :hash');
$result = $query->execute(array(':hash'=>$sha256));
if ($query->rowCount() != 0) die($style->upload_duplicate());
$query = $db->query('SELECT * FROM swf ORDER BY id DESC LIMIT 1;');
$name = $query->fetch(PDO::FETCH_ASSOC);
$new_name = 'uploads/'.($name['id']+1).'.swf';
if(move_uploaded_file($_FILES['file']['tmp_name'], $new_name)) {
$query = $db->prepare('INSERT INTO swf (uploader, upload_time, hash) VALUES (:id, NOW(), :hash);');
$query->execute(array(':id' => $_SESSION['id'], ':hash'=> $sha256));
echo $style->upload_success();
}
else
echo $style->upload_fail();
?>
I can't see why the script would do such echo...
Thank you!
EDIT:
The style_class was the first place where I looked. This class contains functions returning mainly HTML text. The whole class is auto-generated from database.
I'm copying here the upload_* from the generated file, so you can see:
class style_class{
function upload_no_parameter(){
echo "<b>All parameters must be set!</b>";
}
function upload_fail(){
echo "<b>There was an error, please try again.</b>";
}
function upload_success(){
echo "<b>Your SWF has been uploaded!</b>";
}
function upload_duplicate(){
echo "<b>File already exists!</b>";
}
function upload_wrong_format(){
echo "<b>Please upload only SWF files!</b>";
}
}
Thank you!
I'd bet die($style->upload_wrong_format()) is causing the issue. Check that function.
You've got some very nasty logic bugs in your code:
1) Assuming the file upload succeeded. Proper error handling goes like this:
if ($_FILES['file']['error'] !== UPLOAD_ERR_OK) {
die("File upload failed with error code " . $_FILES['file']['error']);
}
Checking any of the other fields in any file upload is not proper - those fields can still be present and populated even for a failed upload. The error codes are documented here: http://php.net/manual/en/features.file-upload.errors.php
2) you're using exec() and calling file to determine mimetypes. Why? PHP has the finfo library for just this purpose: http://php.net/manual/en/book.fileinfo.php it uses the same magic numbers library as file and doesn't require an exec() call to work.
3) You have a very racey error-prone method of getting an ID number for your swf:
$query = $db->query('SELECT * FROM swf ORDER BY id DESC LIMIT 1;');
$name = $query->fetch(PDO::FETCH_ASSOC);
$new_name = 'uploads/'.($name['id']+1).'.swf';
Nothing says that another script cannot execute AND complete in the time you fetch this ID number and the time you complete thigns here. A proper method is to start a transaction, insert a skeleton record into the DB, retrieve its auto_increment primary key, then update the record and do your file moves with that id. It'll be guaranteed to be unique, whereas at some point your code WILL fail and stomp on another upload.
I am trying to display images from my mysql database using php. The image is not getting displayed fully. It gets cut while trying to display an image more than 200 kb (determined from trials , but not too sure).
HTML Code:
<form enctype="multipart/form-data" action="insertimage.php" method="post" name="changer">
<input name="MAX_FILE_SIZE" value="10240000" type="hidden">
<input name="image" accept="image/jpeg|image/jpg|image|JPG|image/png|image/gif" type="file">
<input value="Submit" type="submit">
PHP Code:
<?php
require('myconnect.php');
if (isset($_FILES['image']) && $_FILES['image']['size'] > 0) {
// Temporary file name stored on the server
$tmpName = $_FILES['image']['tmp_name'];
// Read the file
$fp = fopen($tmpName, 'r');
$data = fread($fp, filesize($tmpName));
$data = addslashes($data);
fclose($fp);
// Create the query and insert
// into our database.
$query = "Update whyangry.posts set Photo='$data' where Pid=2";
$results = mysql_query($query, $con);
// Print results
print "Thank you, your file has been uploaded.";
$sql = "SELECT * FROM helpme.posts WHERE Pid=2";
$res = mysql_query($sql,$con);
while ($res1=mysql_fetch_assoc($res))
{
$content = $res1['Photo'];
$id=$res1['Pid'];
}
echo '<img src="data:image/png|image/jpeg|image/gif;base64,' . base64_encode( $content ) . '" />';
echo 'Hello world.';
}
else {
print "No image selected/uploaded";
}
?>
Also i am getting the below error while uploading file in phpmyadmin to a blob datatype
UPDATE `helpme`.`posts` SET `Photo` = 0xffd8ffe000104a46494600010201006000600000ffe10f074578696600004d4d002a0000000800060132000200000014000000564746000300000001000300004749000300000001003200009c9d00010000000e00000000ea1c0007000007f40000000087690004000000010000006a000000d4323030393a30333a31322031333a34373a34330000059003000200000014000000ac9004000200000014000000c0929100020000000335340000929200020000000335340000ea1c0007000007b40000000000000000323030383a30333a31342031333a35393a323600323030383a30333a31342031333a35393a3236000005010300030000000100060000011a00050000000100000116011b0005000000010000011e020100040000000100000126020200040000000100000dd90000000000000048000000010000004800000001ffd8ffe000104a46494600010100000100010000ffdb004300100b0c0e0c0a100e0d0e1211101318281a181616183123251d283a333d3c3933383740485c4e404457453738506d51575f626768673e4d71797064785c656763ffdb0043011112121815182f1a1a2f634238426363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363[...]
MySQL said:
2006 - MySQL server has gone away
Please let me know how to fix the issue. The issue is while displaying images. Whether some size issue is there i dont know please help here.
Using addslashes is nowhere near the correct way to do a SQL query. It will not always work correctly with binary data. I don't know what resource you're using, but it's teaching you very bad habits.
Please DO NOT USE mysql_query in new applications. This is a legacy interface from the 1990s that is in the process of being retired because of the hazards involved in using it incorrectly, something all too easy to do. It's best to use either mysqli or PDO in new projects.
Your query should look like this:
Update whyangry.posts set Photo=? where Pid=?
You can bind to those placeholders when executing the query and avoid having encoding problems. There are many examples on how to do this correctly.