I am using a PHP script that outputs an image, what I am trying to do is track when the image is opened on an email, The code works to display the image but does not run the database query and update the count.
The PHP code as follows.
$image = $_GET['image'];
require_once('connections/site.php');
mysql_select_db($database_site, $site);
$query_requests = "SELECT count FROM tracker WHERE id = '1'";
$requests = mysql_query($query_requests, $site) or die(mysql_error());
$row_requests = mysql_fetch_assoc($requests);
$count = $row_requests['count'];
$newcount = $count++;
$query_update = "UPDATE count SET count = '$newcount' WHERE id = '1'";
$update = mysql_query($query_update, $site) or die(mysql_error());
header("Content-Type: image/jpeg");
readfile('https://mysite.co.uk/images/'.$image);
Maybe I should be using a different method? I was searching around for a way of tracking a standard image open but I couldn't seem to find a decent method so I thought I would try and cook something up to do this.
The problem is that readfile reads and directly writes to the output buffer, hence if you're going to use that method, you'll need to move the readfile to the end of your script.
However, there are a few other concerns:
Your script is using a deprecated database API functions (mysql_*) - you really need to read How can I prevent SQL injection in PHP?
You're potentially exposing other files as you're not attempting to validate what's being fetched via readfile. For example, if $_GET['image'] contains ../connections/site.php, your script will potentially output raw (i.e.: un-parsed) PHP files containing sensitive database settings, etc.) See the existing Preventing Directory Traversal in PHP but allowing paths question/answer for more information.
You're not outputting Content-Type, or Content-Length headers, etc.
put readfile('https://mysite.co.uk/images/'.$image); at the bottom of your code and add this line just before it:
header("Content-Type: image/jpeg");
Related
okay so it is appearing to me my header() isn't doing anything. i tried moving it to the very top of the file instead nothing changed unless I put it in its own but then the rest of the file doesn't run but it does send the header.
EDIT: I updated my code with your suggestions, but it still isn't working correctly. Below is an image of the network tab when I inspect the page. The big thing I notice is the calls are being made but its fetching the same about of data for every image and its a very tiny amount of data.
edit: I am getting and Invalid URL Error when I inspect in chrome unless I remove data:image/png;base64,
Referrer Policy: strict-origin-when-cross-origin
I can't seem to get this working correctly. The png image should be pulling from the database and then displaying in the HTML. (and yes I know this isn't SQL injection safe) I assume there is some issue with it being called from the loop, but I am unsure. Or that it can't find the getImage.php to use it this way do i need to import it or something earlier in the code? It does correctly pull in the item_id. The result just gives me a broken image box. I know my getImage.php is outputting the correctly formatted image, because I have taken the output directly from there and inserted in place of the call for it and the image has show up. So it tells me this line below is my issue
echo ('<td><img src='"getImage.php?image_id="' . $row["item_id"].'"></td><td>');
if I edit as follows it works fine (but obviously it give me the same image for every entry) DBS is the decoded byte string that getImage.php outputs
echo ('<td><img src="data:image/png;base64,DBS"></td><td>');
PHP for the Front Facing page (blackmarket.php:
$sql="SELECT * FROM `item` WHERE 1";
$result = $link->query($sql);
echo("<table>");
if ($result->num_rows > 0) {
// output data of each row
echo("<tr>");
while($row = $result->fetch_assoc()) {
$newrow=0;
echo("<td>");
echo ('<img src="getImage.php?image_id='.$row['item_id'].'" />');
getImage.php:
<?php
require_once "connect.php";
if(isset($_GET['item_id'])) {
$sql = "SELECT item_img FROM item WHERE item_id=" . $_GET['item_id'];
$result = mysqli_query($link, $sql) or die("<b>Error:</b> I can't find the image <br/>" . mysqli_error($link));
$row = mysqli_fetch_array($result);
header("Content-type: image/png");
header("Content-Length: " . strlen($row["item_img"]));
echo $row["item_img"];
}
mysqli_close($link);
Just specify
echo "<td><img src=\"getImage.php?image_id={$row['item_id']}\"></td>";
The browser is responsible for assembling the page, and makes a separate call to the getImage.php script and reads the image data that you are returning.
Return the binary image data directly, along with the size. This might also be part of the problem.
<?php
require_once "connect.php";
if(isset($_GET['item_id'])) {
$sql = "SELECT item_img FROM item WHERE item_id=" . $_GET['item_id'];
$result = mysqli_query($link, $sql) or die("<b>Error:</b> I can't find the image <br/>" . mysqli_error($link));
$row = mysqli_fetch_array($result);
header("Content-type: image/png");
header("Content-Length: " . strlen($row['item_img']));
echo $row['item_img'];
}
mysqli_close($link);
Omit the closing php end tag ?> from this script. PHP best practices is to omit it for all scripts but it is especially important in a script like this that returns binary data, as a bit of whitespace or a UTF-8 BOM character can corrupt your output and is often hard to debug.
You are confusing two things:
data: URIs allow you to embed an image directly in the source code of an HTML page, so that the browser can render them immediately without another call to the server. They use base64 to make the data consist only of text, because you can't put non-text characters in the middle of your HTML source code
Normal https: URLs tell the browser to make a request to the server, which returns the image data, with no extra encoding needed. Most commonly, the web server will load the image straight from disk, but the browser doesn't know about that, so you can just as validly have a PHP script which returns the same data, based on whatever logic you want.
In your code, you've mixed the two: you've prefixed a normal URL with data:, so the browser is expecting the data right there in the source code, and never makes a call to getImage.php.
What you need to do instead is make sure that in your getImage.php you
echo out just the image data - no whitespace or debug output mixed in
not base64 encoded (if it's stored in the database that way, decode it before sending to the browser)
tell the browser it's an image with an appropriate Content-Type header, e.g. header('Content-Type: image/png'); - again, no mention of base64 encoding or anything else, you're serving an image just as if it was a file on your server
You shouldn't need to set the Content-Length header manually, the server will sort that out just like it would if you were outputting HTML
You can then load that image URL directly in the browser and it should display, no need to even look at your HTML until that part is working right.
Well I finally figured out what was going wrong. In my connect.php I had some white space and that white space was being called before my header so my header wouldn't work
I need to hide the url of an audio stream and so I have thought that since I have a download script that uses readfile(); to do this I could do the same but I am not sure how to manipulate this in javascritpt. In fact I have no idea and am not even sure if it is even possible to do it this way.
So far I have a PHP file that is accessed by javascript to get the URL of a track from an ID when the play button is clicked.
$T_ID = mysqli_real_escape_string($con, $_GET['t_id']);
$qry = "SELECT url FROM tracks WHERE T_ID = $T_ID";
$result = mysqli_query($con, $qry);
$row = mysqli_fetch_array($result);
$url = $row['url'];
$path_parts = pathinfo($url); //get path info
$file_name = $path_parts['basename']; //only include mp3 track just incase path added in there
$file_path = 'uploads/' . $file_name; //add path
echo $file_path;
The url from that is just used by javascript to specify the new Audio();. However, anyone that know show to open up the console and view the network tab can just find the URL for the track and download it. Which is not what I want or my users want at all.
My preliminary idea is to replace echo($file_path) with readfile($file_path) but I know that it will somehow need to be manipulated in javascript. If PHP reads the file as binary then can javascript do so too to stream it?
By using readfile(), all you are doing is making the php script into the URL for the download. The javascript Audio() object still fetches that URL (and maybe start playing before it's all loaded) just as it would with a direct url to the file. It's not streaming it. You are just hiding the url to the file.
If you want to offer restricted access, then this is a fine way of doing it, but if anyone can still download using this new URL, then you aren't protecting anything except the actual location of the file.
Also, remember to set headers for the correct mime type, e.g.
header('Content-Type: audio/mpeg');
header('Content-Length: '.filesize($file_path));
readfile($file_path);
You may also want to set cache expiry headers, etc.
I am using the following scripts to test inserting and then reading blob data.
insertion script:
include('session.php');
$provider =$_POST['provider_id'];
$trd_period =$_POST['trading_period_month'];
$pdf_statement =stream_get_contents(fopen($_FILES['pdf_statement']['tmp_name'], 'rb'));
$pdf_statement_clean=addslashes($pdf_statement);
$insert="update rd_provider_statement
set pdf_statement='".$pdf_statement_clean."', creation_user_id='SCO'
where provider_id='".$provider."' and trading_period_month='".$trd_period."'";
mysql_query($insert);
mysql_query("COMMIT");
echo mysql_error();
Download Script:
include('session.php');
//Gather Post Variables
$TP_Month =$_POST["trading_period_month"];
$provider =$_POST["provider_id"];
$TP_format =substr($TP_Month, 0, 7);
//Download Statement
$sql_qry="select *
from rd_provider_statement
where provider='".$provider."' and trading_period_month='".$TP_Month."'";
$sql_err_no=sql_select($sql_qry,$sql_res,$sql_row_count,$sql_err,$sql_uerr);
$row = mysql_fetch_assoc($sql_res);
$bytes =stripslashes($row['pdf_statement']);
header("Content-type: application/pdf");
header('Content-disposition: attachment; filename="'.$provider.'statement'.$TP_format.'"');
print $bytes;
However, when the file is downloaded it cannot open on the grounds that it is not a supported format. I use the basis of the script on another page to download blob data from the database however the insertion into the database here is done by a mysql procedure and not PHP. I think it is my insertion script that is causing the problem.
try using mysql_real_escape_string() instead of addslashes(). it might fix you problem.
For debugging, you might calculate the md5() of the string before inserting into DB and then after retrieving it. I bet you're going to get different hashes, meaning you're not inserting it correctly and your binary data gets corrupted when inserted into the DB.
Side notes:
don't use inserts like that, use binding - How to bind SQL variables in Php?
check for errors and STOP, dont simply echo them(i hope you're doing this in your production code)
Generally you wouldn't want to have any output code before your http header description. See http://php.net/manual/en/function.header.php
Either store the filename and other file information in a session then just access them in another page.
A few things that you need to check:
max_allowed_packet in my.ini should be equal or higher than the file size that you're expecting to store in the database
check to see if the data type that you selected fits the file that you will store. There's tiny blob, blog, medium blob and long blob. You might want to try the largest which is long blob.
I'm not sure about this one but did you already check if file_get_contents works:
mysql_real_escape_string(file_get_contents($file))
Here's my alternative answer.
First the update query:
Prepare the file (assuming that your file is not a binary file):
$tmpName = $_FILES["pdf_statement"]["tmp_name"];
$fp = fopen($tmpName, 'r');
$data = fread($fp, filesize($tmpName));
$data = addslashes($data);
fclose($fp);
$insert="update rd_provider_statement
set pdf_statement='".$data."', creation_user_id='SCO'
where provider_id='".$provider."' and trading_period_month='".$trd_period."'";
DOWNLOAD:
enter code here
$sql_qry="select provider_id, pdf_statement
from rd_provider_statement
where provider='".$provider."'
and trading_period_month='".$TP_Month."'";
$sql_err_no=sql_select($sql_qry,$sql_res,$sql_row_count,$sql_err,$sql_uerr);
$row = mysql_fetch_assoc($sql_res);
$name=$row['provider_id'];
$file=$row['pdf_statement'];
header("Content-Disposition: attachment; filename=\".$name_statement.$TP_format.\";" );
echo $file;
Hope it helps =)
In short, my application needs to pull images from a MySQL db. Since the images pulled will depend on the users location I can't just hardcode image file paths.
So far I've tried using just a PHP script that should allow the app to pull the images from the table into my db but I haven't had any successful results.
In my app this is what I'm using to pull and decode the stream into a bitmap that the phone/app can use:
public static void createIcons(Resources res) throws IOException {
String url1 = " http://mysite.iscool/file-get.php ";
URL ulrn = new URL(url1);
HttpURLConnection con = (HttpURLConnection)ulrn.openConnection();
InputStream is = con.getInputStream();
//end of lines added
markIcon=BitmapFactory.decodeStream(is);
Since I can't seem to return an image to the device from the db using this so far I was wondering what sort of direction I should be going in. I've seen lots of different methods on here explained but I'm not too sure if the method I'm trying to use is even going to work for the purpose I need it to.
Also, I have seen mention of a REST service and other ways to extract images from a mysql db table. So does any one have an suggestions/tutorials/examples of extracting multiple images from a MySQL database?
By the way here is the code I'm using to try to return an image to the device:
<?php
//----------------------------------------------------------
//
//---------------------------------------------------------
// access server
//---------------------------------------------------------
include("fLADB.php");
//
$dbQuery = "SELECT File FROM mark_image_store where imageid =(SELECT MAX(imageid) from
mark_image_store);";
//-----------------------------------------------------------
// selecting into a single element variable
// stores only the first field which is the file data
//-----------------------------------------------------------
$result = mysql_query($dbQuery) or die(mysql_error());
//-----------------------------------------------------------
//reserved for when there is file ext data available.
//to format http headers
//Do not need for use by the phone.
//header("Content-type: image/jpeg");
//if ($ext = "png")
//-------------------------------------------------------------
// use imagejpeg, imagepng?
//-------------------------------------------------------------
header("Content-type: image/png");
print mysql_result($result, 0);
mysql_close();
?>
So after looking at my PHP and Android code can someone please give me some advice on which direction I should be going in next. Should I rework my Android side code and go with a REST service or Webservice or is a PHP script server side enough to handle the image returns.
Any advice/tutorials/or examples would be much appreciated thanks.
You can extract multiple images easily with a single query. But you'll only be able to TRANSFER one image a time, unless you use some kinda of encapsulation like .zip.
If you're serving up the images via HTTP, you'll definitely only be able to serve one picture at a time. There's some tricks where you can serve up a single image with a series of smaller images tiled within it, and display them in restricted-sized divs as tiles/sprites, but that'd require client intervention.
I'd like for my app to be able to tell if an image hasn't been viewed in the last 30 days and remove it (along with data in the DB associated with it). I know you can have PHP read and output the image dynamically but I've heard its quite taxing on the system. Is there a way to for me to track these hits even when the image is viewed directly? (would htaccess be able to do this?) Thanks in advance.
For .htaccess something like this...
RewriteRule ^image/(.*)$ image.php?id=$1
And for PHP...
$id = $_GET["id"]; //don't forget to sanitize
mysql_query("UPDATE images SET views = views + 1 WHERE image = '$id'"); //update no of views
header("Content-Type: image/jpeg"); //send image header
readfile($id); //output file to browser
You can parse out the HTTP logs and do this analysis post-factum.
That said, I'd still recommend going with a "dynamic PHP file outputting an image" approach - note that the PHP file can simply stream out a file. Yes, it'll be slower than not going through PHP at all, but it won't be a significant performance hit on your system.
You can create a script that fetches images and pings your database to show that the image has been viewed. By setting the return type of your script to image/jpg you can just use this script instead of accessing images directly:
<?php
//simplified image display script
$imagepath="phpimages/$id.jpg";
$db->pingImage($id);
$image=imagecreatefromjpeg($imagepath);
header('Content-Type: image/jpeg');
imagejpeg($image);
?>
Then use:
<img src='fetchimage.php?id=478' alt='tracked image' />
As far as I know, you'll need to have a database for this.
Sample schema:
Images
id (int, PK)
path (text)
hits (int)
PHP:
// get_image.php
// note, this is just an example and I'm not considering security or hacking attempts
$sql = mysql_query('SELECT * FROM images WHERE id = '.$_GET['id']);
$img
if(mysql_num_rows($img) == 1)
while($row = mysql_fetch_assoc($sql))
$img = $row;
else
// handle some error
mysql_query("UPDATE images SET hit = hits + 1 WHERE id = ".$_GET['id']);
$img = file_get_contents($img['path']);
header('image/jpg');
echo $img;
There could be errors here, I'm just giving you a general idea...