Any difference between print() and readfile() when loading an image file? - php

Just wondering if there is any difference with the two below code examples:
$image = 'http://www.example.com/image.jpg'
$photo = file_get_contents($image);
ob_start();
header("Content-type: image/jpeg");
print($photo);
ob_end_flush();
or...
$image = 'http://www.example.com/image.jpg'
$photo = file_get_contents($image);
ob_start();
header("Content-type: image/jpeg");
readfile($photo);
ob_end_flush();

readfile's parameter is a filename, not the content itself. Therefore, you'd call it like this:
$image = 'http://www.example.com/image.jpg'
ob_start();
header("Content-type: image/jpeg");
readfile($image);
ob_end_flush();
Since readfile reads and write chunks at a time, its memory consumption will be constant, whereas when you store the result of file_get_contents into $photo, you'll need to have enough memory to store the image.
In your case, the output buffering makes the file_get_contents variant demand twice as much memory as the image's size. For a large image, readfile therefore halves the memory requirements. Note that your output buffering means that the download will be delayed. If you don't need it for anything else, you will get better performance (both in actual speed and server memory requirements) if you simply disable it:
$image = 'http://www.example.com/image.jpg'
header("Content-type: image/jpeg");
if (#readfile($image) === false) {
// print error image
}

There is a very significant difference in that readfile takes the filename as the argument.
The second snippet should be something like
$image = ...
readfile($image)
This has the advantage of not having to store the entire file contents in memory (a string) since readfile emits it immediately. Of course, if you buffer the output that is no longer true.

In the first instance your code would never work
readfile($photo);
^--------------- Takes file name not string
PHP DOC SAYS
Reads a file and writes it to the output buffer.
You don't need to re event that and duplicate that with multiple other functions its just saying
readfile = file_get_contents + print
It just like using fopen instead of file_get_contents just a get a simple content in a file
Lastly readfile is faster tested with 10000 loop on same image
Single Run
Array
(
[m1] => 0.0006101131439209
[m2] => 0.00031208992004395
)
Dual Run
Array
(
[m1] => 3.4585757255554
[m2] => 2.9963381290436 <---- Faster
)
m1 & m2 functions
function m1($array) {
$photo = file_get_contents('a.png');
ob_start();
print($photo);
ob_end_clean();
}
// Array clean copy
function m2($array) {
ob_start();
readfile('a.png');
ob_end_clean();
}

Related

How to output a massive file with PHP without running out of memory

I have the code below to output a big file, but it's falling over because PHP's memory use seems to grow and grow as the file is read:
<?php
// various header() calls etc.
$stream = fopen($tarfile,'r');
ob_end_flush();
while (!feof($stream)) {
$buf = fread($stream, 4096);
print $buf;
flush();
unset($buf);
$aa_usage = memory_get_usage(TRUE); // ← this keeps going up!
}
fclose($stream);
I had thought that by the combination of flush and unset the additional memory use would be limited to the 4k buffer, but I'm clearly wrong.
If all you need is to output the content of a file then the right tool to do it is the PHP function readfile(). Replace all the code you posted with:
readfile($tarfile);
As the documentation says:
Note:
readfile() will not present any memory issues, even when sending large files, on its own. If you encounter an out of memory error ensure that output buffering is off with ob_get_level().
You can try load only as much data as you need first, and if you load more data use the function: fseek()

Image not showing ob_start()

Couldn’t find anything on the webs so here is the issue: I have a cropper tool and I want to show the cropped image on this page. But because my functions.php has a function that uses a header method, I had to use ob_start in my file. That causes the problem that my image is not shown (it’s a question mark right now, not the right image).
Code:
<?php
ob_start();
require_once("includes/session.php");
require_once("includes/connection.php");
require("includes/constants.php");
require_once("includes/functions.php");
confirm_logged_in();
require_once("includes/header.php");
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$targ_w = $_POST['w'];
$targ_h = $_POST['h'];
$jpeg_quality = 90;
$src = $_POST['image'];
$ext = end(explode(".", $_POST['image']));
switch($ext) {
case 'jpg';
$img_r = imagecreatefromjpeg($src);
$dst_r = imagecreatetruecolor($targ_w, $targ_h);
imagecopyresampled($dst_r,$img_r,0,0,$_POST['x'],$_POST['y'],
$targ_w,$targ_h,$_POST['w'],$_POST['h']);
header('Content-type: image/jpeg');
imagejpeg($dst_r,null, $jpeg_quality);
$output = ob_get_contents();
break;
case 'png';
$img_r = imagecreatefrompng($src);
$dst_r = ImageCreateTrueColor($targ_w, $targ_h);
imagecopyresampled($dst_r,$img_r,0,0,$_POST['x'],$_POST['y'],
$targ_w,$targ_h,$_POST['w'],$_POST['h']);
header('Content-type: image/png');
imagepng($dst_r, null, 8);
$output = ob_get_contents();
break;
}
}
echo $output;
ob_end_clean();
?>
Given how your code is indendent:
<?php ob_start(); ?>
<?php require_once("includes/session.php"); ?>
[...snip...]
$targ_h = $_POST['h'];
Those 4 spaces before the <?php ob_start call are output and disabling your subsequent header() calls.
Plus, nowhere do you actually OUTPUT your image data:
$output = ob_get_contents();
echo $output; // <----you need this
When outputting binary files, make sure you output only the data you want to:
The first thing in your code should be <?php and you should never leave the PHP code. No ?> should be used, more below.
When using Unicode in your script, be sure not to include BOM.
Do not use the closing ?>. It is not necessary. If you use it, anything after that is sent to output. This includes a newline character at the end of the last line of the script.
If your code is designed poorly and generates any garbage before outputting the data, buffer the garbage generation and then throw away the buffer just before outputting the data.
Now to your code specifically:
In switch statement, the case part is ended by a colon, not semicolon. I.e. write case 'jpg': instead of case 'jpg';.
The ob_end_clean(); at the end of your script should be moved right after the last require_once call. The includes are producing garbage, the rest of the output is wanted; therefore you should buffer only the output generated by the includes and then throw the buffer away and let the rest be unbuffered.
Delete the lines $output = ob_get_contents(); (occurs twice) and echo $output;. After performing the previous change, they are not needed anymore, they just produce errors.
Try it and see if it helps. If not, comment on this answer and we’ll try to find your problem.
ob_start starts output buffering. ob_end_clean cleans the buffer and stops output buffering without sending anything to the client, so you basically discard any output.
I think you meant to use ob_end_flush instead of ob_end_clean, which sends the output buffer to the client instead of just ending buffering.
Since you used ob_get_contents to put the output in a variable, you could opt to echo that variable after calling ob_end_clean, but that will make your script just larger, less clear and more memory-consuming, since you then have the contents of the entire image in the output buffer and in the $output variable. So I think using ob_end_flush really is the better option.
update ur code like this:
......
switch($ext)
{
case 'jpg';
ob_start();
......
cannot have output before header().
aha, my English sucks.
But because my functions.php has a function that uses a header method,
I had to use ob_start in my file. That causes the problem that my
image is not shown (it’s a question mark right now, not the right
image).
Both statements are not really true. First: if you have a function that uses a header method, don't worry, as long as the function is not executed, your script doesn't care about the header method. And if it gets executed, ob_start wont help, because
While output buffering is active no output is sent from the script
(other than headers), instead the output is stored in an internal
buffer.
(http://www.php.net/ob_start) Note the "other than headers". So the real problem is not you have a header method, but that you have some output in one of your includes, either a whitespace or any other output, e.g. "includes/header.php" sounds like it may output the html header.
So first thing would be to remove all output from these files, and then remove all output buffering functions from your script.
So really you don't need output buffering, but even if you would need it, output buffering is not the cause that your image is not shown, output buffering works fine, but in your code you output the image before ob_end_clean, thus discarding any output.
And if you really by no means can remove the output from the includes, just call ob_end_clean right after the includes and continue as usual without output buffering.

PHP performance: gzcompress() vs ob_start('ob_gzhandler')

I'm wondering, how to compress my output best.
Usually I just add ob_start('ob_gzhandler') at the top of my index.php, to compress the whole output.
I'm using a simple caching-Class to store the generated HTML in a file (index.cache.htm) instead of rebuilding it on every refresh. The content of index.cache.htm is minified due to a better performance.
Couldn't I compress the cached content instead of using ob_start('ob_gzhandler')?
Example 1 (caching the buffered output):
ob_start(); // start buffer
$b = ob_get_contents(); // get buffer
ob_end_clean(); // free buffer
$b = CustomHTMLMinifyFunction($b); // minify the HTML
$b = gzcompress($b); // compress the HTML
file_put_contents('index.cache.htm', $b); // save file
Example 2 (caching explicit data):
$d = 'Some data, e.g. JSON'; // some data
$d = gzcompress($d); // compress data
file_put_contents('data.cache.txt', $d); // save file
What's the difference or best practise? Thanks in advance!
Edit: Does it ever make sense to store the compressed data in a file? Or is it only useful while outputting the data?
ob_start:
The [callback] function will be called when the output buffer is flushed (sent) or cleaned (with ob_flush(), ob_clean() or similar function) or when the output buffer is flushed to the browser at the end of the request.
In other words, ob_get_contents() will return the output buffer uncompressed contents:
$log = 0;
function callback($input){
global $log;
$log += 1;
return ob_gzhandler($input);
}
ob_start('callback');
$ob = ob_get_contents();
echo $log; // echoes 0, callback function was not called
You must compress the output of ob_get_contents() if you want to cache a compressed version of the output data.
But you must configure your web server so that it is aware the files are pre-compressed (instructions for Apache). You can't just send compressed files to your client without setting proper headers.
To answer your edit, it makes sense to pre-compress your cache, otherwise the content is compressed on the fly. Also keep in mind that some clients do not support compression: you should keep an uncompressed version of your files if you want to be able to serve them.

imagepng Very Slow

I have a .php file that's supposed to load an image for display in an img tag(i.e., <img src="the_file.php?which=0"/>). It looks like this:
<?php
ob_clean();
header("Content-type: image/png");
include_once("util.php");
//Do a simple calculation to get $name from (int)$_GET["which"];
$im = imagecreatefrompng("protected_directory/".$name.".png");
imagepng($im,NULL,0,NULL);
imagedestroy($im);
ob_end_flush();
?>
It works correctly, but the image loads substantially slower than just loading it directly(i.e. <img src="protected_directory/the_name.png"/>, where "the_name" was calculated the same way as in the PHP file, but I can't just do this because the protected_directory isn't world readable).
My question is, why is this suddenly so much slower? It's not a large image, but nor is it terribly small.
If you're just displaying an existing file, use readfile() to output it to the browser. There's no need to go through all the overhead of creating an editable GD object for this.
imagepng is known to be slow, if you need to output images with a PHP script, use code like this:
$filename = md5(time() . mk_rand());
imagepng($im, $filename);
echo file_get_contents($filename);
As another answer, I figured out that you can use the third parameter to compress the image (PNG uses zlib). Setting it to 9 works about as well as the other solutions.

PNG manipulation removes transparency

I am simply trying to return an PNG image through PHP, but I have a problem with the transparency not showing right. (Basically one PHP file will be capable of returning any of my images.)
I use simple code to return the image:
<?php
$im = imagecreatefrompng("images/fakehairsalon.png");
header('Content-Type: image/png');
imagepng($im);
imagedestroy($im);
?>
The original image looks like this:
And the one returned via PHP (and that piece of code) looks like this:
Is there anything I can do to prevent this and make the image come through normal?
As explained in a user comment, you must do this:
<?php
$im = imagecreatefrompng("borrame.png");
header('Content-Type: image/png');
imagealphablending($im, true); // setting alpha blending on
imagesavealpha($im, true); // save alphablending setting (important)
imagepng($im);
imagedestroy($im);
?>
Update: This answer was assuming that your code is an except from a bigger script you are using to do on-the-fly image manipulation.
If you don't want to change the original file, this is a plain waste of memory and CPU cycles. You can use file system functions to read it as a regular file, such as readfile().
It's also worth noting that using PHP to deliver a file only makes sense if you want to do something else as well, such as:
Restricting access to the file
Keeping a counter

Categories