Solved

# php: How do I display binary data as an image (png) on a browser?

Posted on 2008-10-09
4,063 Views
I am building a website for a corporation that would like to display a stock chart on one of its pages.

It returns an XML file where the tag ChartImage contains a png image.

The closest I have come to displaying it is to show a series of binary data on the browser:

Question: Would your code "translate" the binary data to png?

Here is my code so far:

mainpage.php (I have included 3 versions of trying to embed the data; the third one -include- returns binary data-)
------------------
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<body >
<div id="wrapper">
<div class="main">
<div class="mainRight">
<H1>SHARE QUOTES AND CHARTS</H1>

<object type="image/png" data="../stock/chart.php" width="100" height="100"></object>

<img src="../stock/chart.php" width="420px" height="400px" border="1"/>

<?php include "../stock/chart.php" ?>

</div>

chart.php
------------
function value_in($element_name,$xml, $content_only = true) { if ($xml == false) {
return false;
}
$found = preg_match('#<'.$element_name.'(?:\s+[^>]+)?>(.*?)'.
'</'.$element_name.'>#s',$xml, $matches); if ($found != false) {
if ($content_only) { return$matches[1];  //ignore the enclosing tags
} else {
return $matches[0]; //return the full pattern match } } // No match found: return false. return false; }$xml = file_get_contents('http://www.stockwatch.com/webservice/CorporateServices.asmx/CorporateChart?Id=xyz&Pw=123&pars=width=420%26ph=400%26ih=100%26npdivs=20%26bc=0xfefefe%26fc=0x007dd0%26tc=ox007dd0%26sf=a%26sfs=8');

$channel_ChartImage = value_in('ChartImage',$xml);

echo $channel_ChartImage; ?> Thank you, Ina 0 Question by:InaByDesign • 16 • 14 30 Comments LVL 39 Expert Comment ID: 22682783 XML can not store binary data directly, it must be encoded. The most common encoding used for binary data in XML is base64. PHP has a standard function for decoding base64-encoded data: http://se2.php.net/manual/en/function.base64-decode.php 0 Author Comment ID: 22682823 If I decode the binary data I still do not get a picture: Data before encoding: iVBORw0KGgoAAAANSUhEUgAAAaQAAAIfCAYA ... 6nfvmXtlf9pcPyAc+ xgc+RlF9bkKfm5APyAfkA1f7wP8H9aWOxVfjZ80AAAAASUVORK5CYII= Data after encoding: ÿýPNG  ýýý IHDRýýýýý ýýý?ýý ýýýsRGBýýý ýýýýgAMAýýýý ýaýýý cHRMýýz&ýýýýýýýýýýýýýýu0ýýýýý:ýý .... ýtýýýýý|ýCRýHýhýýýý@ýv&9ýfu)ý>;xýýýýýýmy÷ýýý>ýý{ý'ýtýLý{+ ýfuýýý^ý_ýý>ý>FQ}nBýýýýýWýýýýýWýgýýýýýIENDýBý Modified code: <?php //header('Content-type: image/png'); function value_in($element_name, $xml,$content_only = true) {
if ($xml == false) { return false; }$found = preg_match('#<'.$element_name.'(?:\s+[^>]+)?>(.*?)'. '</'.$element_name.'>#s', $xml,$matches);
if ($found != false) { if ($content_only) {
return $matches[1]; //ignore the enclosing tags } else { return$matches[0];  //return the full pattern match
}
}
// No match found: return false.
return false;
}

$xml = file_get_contents('http://www.stockwatch.com/webservice/CorporateServices.asmx/CorporateChart?Id=AeroplanXML&Pw=1000842&pars=width=420%26ph=400%26ih=100%26npdivs=20%26bc=0xfefefe%26fc=0x007dd0%26tc=ox007dd0%26sf=a%26sfs=8');$channel_ChartImage = value_in('ChartImage', $xml);$image_64 = base64_decode($channel_ChartImage); echo$image_64;
?>

0

LVL 39

Expert Comment

ID: 22682943
The 'PNG' at the start of the decoded data indicates that the conversion was correct.

You need to un-comment the first line:

0

Author Comment

ID: 22683002
I still do not get an image:

The output on the browser is the following:

from <img src="../images/layout/clear.gif" width="100%" height="18px" /> ==> nothing
from <object type="image/png" data="../stock/chart.php" width="100" height="100"></object> ==> nothing
from <img src="../stock/chart.php" width="420px" height="400px" border="1"/> ==> empty box in FF
from <?php include "../stock/chart.php" ?> ==> the following in FF: ÿ
Warning: Cannot modify header information - headers already sent by (output started at E:\XAMPP\htdocs\aeroplan\ga\pages\invQuote.php:8) in E:\XAMPP\htdocs\aeroplan\ga\stock\chart.php on line 1
ýPNG  ýýý IHDRýýýýýýýý?ýýýýýsRGBýýýýýýýgAMAýýýý ýaýýý
the following in IE7: Warning: Cannot modify header information - headers already sent by (output started at E:\XAMPP\htdocs\aeroplan\ga\pages\invQuote.php:8) in E:\XAMPP\htdocs\aeroplan\ga\stock\chart.php on line 1
ýPNG  IHDRý?ýýsRGBýýýgAMAýý ýa cHRMz&ýýýýýu0ý:ýpýýQ<ýIDATx^ýýaý$)ýlý-ýýaýý|ýý"ýýýýýýý 0 LVL 39 Expert Comment ID: 22683147 Use only <img src="../stock/chart.php" width="420px" height="400px" border="1"/> in your html, remove the <object> element and the PHP include. The latter is probably the cause of the warning. There seems to be a space in the base64 data provided above, before the decoding, it should not be there... Try using the below function to check if the bas64 encoded data is valid. Insert the comment on the header() function again, and run/include chart.php to see the output. function is_base64_encoded($data) {
return preg_match('%^[a-zA-Z0-9/+]*={0,2}$%',$data));
}

$channel_ChartImage = value_in('ChartImage',$xml);

echo is_base64_encoded($channel_ChartImage) ? 'This is a valid base64 string': 'This is NOT valid base64';  0 LVL 39 Expert Comment ID: 22683186 Btw, the correct <img> should be like this, no "px" and no border attribute, but you need an alt attribute (mandatory according to html spec): <img src="../stock/chart.php" width="420" height="400" alt="Bad image data" />  0 Author Comment ID: 22683237 When I ran the function preg_match I get "This is a valid base64 string" ==> so that is good No image yet: mainpage.php ------------------ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body > <div id="wrapper"> <div class="main"> <div class="mainRight"> <H1>SHARE QUOTES AND CHARTS</H1> <img src="../stock/chart.php" width="420px" height="400px" border="1"/> </div> <!--class main --> </div><!--class wrapper --> </html> <?php header('Content-type: image/png'); function is_base64_encoded($data) {
return preg_match('%^[a-zA-Z0-9/+]*={0,2}$%',$data);
}

function value_in($element_name,$xml, $content_only = true) { if ($xml == false) {
return false;
}
$found = preg_match('#<'.$element_name.'(?:\s+[^>]+)?>(.*?)'.
'</'.$element_name.'>#s',$xml, $matches); if ($found != false) {
if ($content_only) { return$matches[1];  //ignore the enclosing tags
} else {
return $matches[0]; //return the full pattern match } } // No match found: return false. return false; }$channel_ChartImage = value_in('ChartImage', $xml); //echo is_base64_encoded($channel_ChartImage) ?
//  'This is a valid base64 string':
//  'This is NOT valid base64';

$xml = file_get_contents('http://www.stockwatch.com/webservice/CorporateServices.asmx/CorporateChart?Id=ABC&Pw=123&pars=width=420%26ph=400%26ih=100%26npdivs=20%26bc=0xfefefe%26fc=0x007dd0%26tc=ox007dd0%26sf=a%26sfs=8');$channel_ChartImage = value_in('ChartImage', $xml);$image_64 = base64_decode($channel_ChartImage); echo$image_64;
?>
0

LVL 39

Expert Comment

ID: 22683395
Change your <img> tag, include an alt attribute. Is the value of the alt attribute output where the image should have been?

There is a missing </div> in your html, but that is not the reason for your problem.

Make sure there is no newline after the ?> in chart.php.

Try adding this before the echo statement:
header('Content-Length: '.strlen($image_64;));  0 LVL 39 Expert Comment ID: 22683400 Sorry, one ; too much: header('Content-Length: '.strlen($image_64));

0

LVL 39

Expert Comment

ID: 22683449
A blank line before <?php will break the image, make sure <?php is on the very first line of chart.php.
0

Author Comment

ID: 22683475
No, still nothing, but it displays 'chart data' thanks to the alt tag

Change your <img> tag, include an alt attribute. Is the value of the alt attribute output where the image should have been?
==> <img src="../stock/chart.php" width="420" height="400" alt="chart data" />

There is a missing </div> in your html, but that is not the reason for your problem.
==> thanks fixed it

Make sure there is no newline after the ?> in chart.php.
==> no newline; ?> is the last item

Try adding this before the echo statement:
==> done:

function is_base64_encoded($data) { return preg_match('%^[a-zA-Z0-9/+]*={0,2}$%', $data); } function value_in($element_name, $xml,$content_only = true) {
if ($xml == false) { return false; }$found = preg_match('#<'.$element_name.'(?:\s+[^>]+)?>(.*?)'. '</'.$element_name.'>#s', $xml,$matches);
if ($found != false) { if ($content_only) {
return $matches[1]; //ignore the enclosing tags } else { return$matches[0];  //return the full pattern match
}
}
// No match found: return false.
return false;
}

$channel_ChartImage = value_in('ChartImage',$xml);

$xml = file_get_contents('http://www.stockwatch.com/webservice/CorporateServices.asmx/CorporateChart?Id=abc&Pw=123&pars=width=420%26ph=400%26ih=100%26npdivs=20%26bc=0xfefefe%26fc=0x007dd0%26tc=ox007dd0%26sf=a%26sfs=8');$channel_ChartImage = value_in('ChartImage', $xml);$image_64 = base64_decode($channel_ChartImage); header('Content-Length: '.strlen($image_64));
echo $image_64; ?> 0 LVL 39 Expert Comment ID: 22683562 hm... try running chart.php directly in the browser. Still no image output? Try writing the data to a file, and check if the file is a valid png file, i.e. if it is viewable. $image_64 = base64_decode($channel_ChartImage);$f = fopen('test_chart.png','w');
fwrite($f,$image_64);
fclose($f);  0 Author Comment ID: 22683591 It DID create the file test_chart.png successfully. It opens fine, is viewable and is exactly as should be. 0 LVL 39 Expert Comment ID: 22683628 ok, thats good. Check the image src attribute, is the path "../stock/chart.php" correct? If chart.php is in the same directory as the html page, it should be only "chart.php". 0 Author Comment ID: 22683645 It is not in the same directory, the path is correct. 0 LVL 39 Expert Comment ID: 22683709 Can you post your current chart.php as an attachment? I'll replace file_get_contents() with some local png file I have, and check if it will run for me. 0 Author Comment ID: 22683744 Here you go! chart.php.txt 0 LVL 39 Expert Comment ID: 22683767 Why is the Content-Length header at the start? That won't work, the$image_64 is not defined yet. This header must come after this line:

$image_64 = base64_decode($channel_ChartImage);

Also, you must remove the file writing and include the echo... try this:
<?php

function value_in($element_name,$xml, $content_only = true) { if ($xml == false) {
return false;
}
$found = preg_match('#<'.$element_name.'(?:\s+[^>]+)?>(.*?)'.
'</'.$element_name.'>#s',$xml, $matches); if ($found != false) {
if ($content_only) { return$matches[1];  //ignore the enclosing tags
} else {
return $matches[0]; //return the full pattern match } } // No match found: return false. return false; }$xml = file_get_contents('url goes here');
$channel_ChartImage = value_in('ChartImage',$xml);
$image_64 = base64_decode($channel_ChartImage);
header('Content-Length: '.strlen($image_64)); echo$image_64;

?>

0

Author Comment

ID: 22683789
no... it did not work

0

LVL 39

Expert Comment

ID: 22683804
Did you remember to replace 'url goes here' with the actual url?
0

Author Comment

ID: 22683828
yes, and I am attaching the 'calling' php
main.php.txt
0

LVL 39

Expert Comment

ID: 22683860
I think this part is ok. When you run chart.php directly in the browser, you should get the image.

I think it is very strange, writing $image_64 to a file results in a valid png file, but echoing it does not... If you remove the content-type header again and call chart.php directly in the browser, do you se "rubbish" with PNG at the start? Or just a blank screen? 0 Author Comment ID: 22683939 No I do not get the image. I removed both headers, otherwise I get warnings. 0 LVL 39 Expert Comment ID: 22683959 Without the headers, it shouldn't output an image. If you remove the headers and call chart.php DIRECTLY in the browser, do you se "rubbish" with PNG at the start? Or just a blank screen? 0 Author Comment ID: 22683969 "rubbish" with PNG at the start: ýPNG  ýýý IHDRýýýýý ýýý?ýý ýýýsRGBýýý ýýýýgAMAýýýý ýaýýý cHRMýýz&ýýýýýýýýýýýýýýu0ýýýýý:ýýýpýýQ<ýýýIDATx^ýaý$)ýlý-ýýaýý|ýý"ýýýýýýýsýLW8ýýýpýýýýýýýýýý, ýýýýýý'ýýýýýý ýýýýýýOýý|@> ýýýH@ýýv>SýýýýLýý\lýý»ýVýu6ýý^}ý2cýýýýýýýYý
0

LVL 39

Expert Comment

ID: 22684023
ok, thats good. That means we probably have the correct data, we just have problems outputting it correctly.

I'm running out of ideas here, so I am thinking about a work-around.

What if you create a file, and the <img> references the png file directly? You could check the age of the file, and re-create it every x minutes, depending on how often the data returned from the web service changes. You could include the company id in the file name. This would also ease the burden on the server. Is this a feasable approach?
0

Author Comment

ID: 22686398
If we can successfully create a file and then read it can we not somehow bypass that step and show the result on the screen?

The following code works, but I how does writing to a file withstand heavy traffic?:
0

LVL 39

Accepted Solution

Roger Baklund earned 500 total points
ID: 22686878
You probably don't need to write to the disk every time, when the same chart is displayed within a short time frame (how short?) you can skip the writing and just use the file created by a previous request.

Writing to a file every now and then is probably (depends on hardware) less work for the server than the combination of accessing the web service,  parsing the xml and base64-decoding it EVERY time.

So, how often would you need to re-create the file? Every hour? Every 10 minutes? See code below.

Is there only one chart per company? If there are more, the file name must include something more than the company id, something to make it unique for each chart.
<?php //header('Content-type: image/png');

function value_in($element_name,$xml, $content_only = true) { if ($xml == false) {
return false;
}
$found = preg_match('#<'.$element_name.'(?:\s+[^>]+)?>(.*?)'.
'</'.$element_name.'>#s',$xml, $matches); if ($found != false) {
if ($content_only) { return$matches[1];  //ignore the enclosing tags
} else {
return $matches[0]; //return the full pattern match } } // No match found: return false. return false; }$company_id = 'abc';

$filename = 'chart_'.$company_id.'.png';
$cache_time = 10 * 60; # 10 minutes$refresh = true;
if(file_exists($filename)) { # check if re-create is needed$stat = stat($filename); if($stat['mtime'] > time() - $cache_time)$refresh = false;
}
if($refresh) {$xml = file_get_contents('http://www.stockwatch.com/webservice/CorporateServices.asmx/CorporateChart?'.
'Id='.$company_id. '&Pw=123&pars=width=420%26ph=400%26ih=100%26npdivs=20%26bc=0xfefefe%26fc=0x007dd0%26tc=ox007dd0%26sf=a%26sfs=8');$channel_ChartImage = value_in('ChartImage', $xml);$image_64 = base64_decode($channel_ChartImage);$f = fopen($filename,'w'); fwrite($f,$image_64); fclose($f);
}

echo '<img src="'.\$filename.'" alt="chart data" />';
?>
`
0

Author Comment

ID: 22687456
I just found out that the chart needs to be updated only once a day!

There is only one company and only one chart.

So your solution (which started as a workaround) is better than what I wanted to implement in the first place.

Writing to a file once a day has to be less work for the server than the combination of accessing the web service,  parsing the xml and base64-decoding it EVERY time.

Thank you and you deserve your points!
0

Author Closing Comment

ID: 31504844
Thank you very much!
This was a rush job and I really appreciate your help.
0

## Featured Post

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Any business that wants to seriously grow needs to keep the needs and desires of an international audience of their websites in mind. Making a website friendly to international users isn’t prohibitively expensive and can provide an incredible return…
Developer portfolios can be a bit of an enigma—how do you present yourself to employers without burying them in lines of code?  A modern portfolio is more than just work samples, it’s also a statement of how you work.
This tutorial demonstrates how to identify and create boundary or building outlines in Google Maps. In this example, I outline the boundaries of an enclosed skatepark within a community park.  Login to your Google Account, then  Google for "Google M…
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.