I am trying to cache images which have been generated. You create a image by accessing the file by resize.php?width=x&height=y.
If the image of that width and height does not exist I use imagemagick to create it. However if it does exist it is served to the visitor.
The !file_exists($name) check works fine so processing is not done when its not needed. However the images still take a while to load.
Is my approach of readfile incorrect or have I set headers incorrectly?
if (!file_exists($name)) {
//image processing here
}
header("Content-Type: image/png");
header("Expires: Sat, 25 Jul 2020 10:00:00 GMT");
readfile($name);
Thanks.
If i had to do this, i'd proccess the image if it does not exist save it in some web accessible folder. If the file exists just redirect to it
header( 'Location: http://www.yoursite.com/path/to/existing/file.png' )
Related
We have had a site for many years, PHP on Ubuntu where clients can download forms/letters as pdfs, then upload them when completed.
Now when re-downloading client's uploaded multi-page pdf, the first (cover) page is blank and Adobe acrobat says "error exists on page...". When we ftp the document, the file is fine with no errors, so the problem has to be in the file download / readfile() process.
This error occurs in both Chrome & Firefox (all updated). If you just view the pdf in the browser, the cover page appears to be fine and readable, however, if you download it you get the same adobe error and blank first page. I am at a loss for how to diagnose this.
Below is the PHP code we use to pull the file.
Please note, for security reasons we do not allow linking directly to any files. The link is to a php page that validates an encoded file request for permission to read a file, then pulls the correct file info from the database for that user, then reads the file and serves it back to the client. I have not included the validation or database related code, just the relevant readfile code:
header("Content-Length: " . filesize('/filestore/' . $doc['filename']));
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
header('Content-type: application/pdf');
header('Content-Disposition: attachment; filename="' . $docname . '.' . strtolower(end(explode('.', $doc['filename']))) . '"');
header("Pragma: private");
readfile('/filestore/' . $doc['filename']);
Additional info based on questions in comments:
To prevent direct linking and brute force scraping of documents, the file store is not in a public folder (so no direct linking).
User links look something like this My File with the id encoded with the file info and CSRF token plus session token checks as part of the validation. The download.php file validates the user and file to make sure they have permission to the document then calls the code above to obtain and "serve" the file to the browser.
$docname is the client's original filename for the file stored in the database. We save files with our own generated file name to prevent naming conflicts when clients use the same file name, this is stored in the DB as $doc['filename']. We restore the client's filename when downloading.
I'm in the process of developing a PHP webpage that constructs a .SVG file from a SQL database on the fly, embeds it in the page, and enables a user to interact with it. These temporary files take on the form SVG[RandomNumber].svg and the unlink function successfully deletes the files with no error messages.
Here's the problem: I assumed that if I invoked the unlink function after the SVG file had loaded for the user, the webpage would be unaffected since the user's browser would have cached the file or whatnot. Everything works perfectly when no unlink command is present in the code; however, 'unlinking' anywhere -- even at the end of the webpage, causes no object to show at all. In Firefox there's no trace of the object, and in IE I receive the error "The webpage cannot be found."
So have I deleted the file before the browser uploads it? What's the best way to deal with the general situation?
Thank you.
It might be useful to change workflow and don't create temporaries. When image is used only once or it's generation is not a big deal you can try to generate it on-the-fly in following fashion
<?php
// We'll be outputting a SVG
header('Content-type: Content-Type: image/svg+xml');
// It will be called image.svg
header('Content-Disposition: attachment; filename="image.svg"');
// Don't cache
header("Cache-Control: no-cache, must-revalidate");
header("Expires: " . date("D, j M Y H:i:s"));
// The PDF source is in original.pdf
generate_svg_from_db('image.svg');
?>
I am trying to build an application in which i have to stream the media files (audio and video) to the browser. I am reading the file through php and send the data to browser. I am using the following code.
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
header("Content-Type: {$file->getMimetype()}");
header("Content-Disposition: inline; filename=".$filename.";");
header("Content-Length: ".strlen($file_content));
echo $file_content;
Every thing is working fine, except when i try to forward the video or audio, (I mean suppose current play location is 0:15 and it directly go to 1:25), media stops and when i press the play button again, it starts from the beginning.
I think the problem is with the buffering, but can't figure it out. Am i doing something wrong in header or something else is required.
Thanks.
I think you need to implement the Range header, so that the client can skip to a specific position in the file. You can probably find out what goes wrong by sniffing the request the player sends.
What you want is called "Content-Range requests"
Have a look here Resumable downloads when using PHP to send the file?
I came across this recently which may help you:
http://www.jasny.net/articles/how-i-php-x-sendfile/
Rather than passing the whole file through PHP (which eats up memory), you can use x-sendfile. This is an Apache module which allows you to run a PHP program, but pass control back to the web server to handle the actual file download once your code has done what it needs to do (authentication, etc).
It means that your PHP code doesn't have to worry about how the file is served; let the web server do what it's designed for.
Hope that helps.
Here is a good tutorial for it, you only want the PHP section but still:
http://www.devshed.com/c/a/PHP/Video-Streaming-PHP-Script-Tutorial/3/
I've created a function that rotates defined image. It works perfect in firefox, but in IE and Opera nothing happens - the image is reloaded but not rotated. Does anybody know why? Here goes the code:
function rotateImage($direction, $id, $angle) {
$dir = opendir($direction);
if ($img = imagecreatefromjpeg($_SESSION['files'][$id]['large'])) {
$width = imagesx ( $img );
$height = imagesy ( $img );
$rotate = imagerotate($img, $angle, 0);
imagejpeg($rotate, $_SESSION['files'][$id]['large'], 100);
}
else {
echo '<p>Error: image cannot be rotated</p>';
}
closedir($dir);
}
The problem is definitely not with the browser you are using as the rotation is done server-side.
You might be running into a caching issue or an issue with the code used to call that function.
Are you:
Using JavaScript to initiate a reload?
Your JavaScript code might be the issue here.
Sending the proper no-cache headers?
If not, you might be running into a situation where the image is cached on the browser, which is why you are not seeing your changes. Either send the proper Cache-control and Expires headers, or append a random identifier to the image url (?_=$x where $x = time() will work fine... Headers are preferred).
Sending the proper Content-type header?
Not sending the proper headers might cause erratic behavior in some browsers. You might want to try using header('Content-type: image/jpeg')
Sending only the image data without any extra characters?
Make sure you don't output anything else than the image. Your output stream must not have any extra characters, including whitespaces.
Try hit refresh! Or clear cache and reload.
This is because the image is saved in browsers cache, and browser know it has it, but it doesn't know it has been changed. One of the tricks is to save the image on the server side with randomly generated name.
I would suspect you aren't sending an appropriate Content-Type header for the image. Alternatively, the image may be slightly corrupted (commonly caused by spaces before/after the php tags in your source code). Save the image from Firefox on your hard drive, open it in a text editor (such as Editplus) and check it doesn't start or end with a space.
PHP is server side, so if it works on one browser the code works fine and the issue lies with the browser. I would assume that IE and opera are caching the image. If possible set the headers for the images so that they don't get cached:
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
I want to monitor how often some external images are loaded.
So my idea is instead of giving a uri directly like this:
www.site.com/image1.jpg
I can create a PHP script which reads the image, so I built a PHP file and my HTML would look like this:
<img src="www.site.com/serveImage.php?img=image1.jpg">
but I don't know how to read the image from disk and return it. Would I return a byte array or set the content type?
Kind regards,
Michel
Sending images through a script is nice for other things like resizing and caching on demand.
As answered by Pascal MARTIN the function readfile and these headers are the requirements:
Content-Type
The mime type of this content
Example: header('Content-Type: image/gif');
See the function mime_content_type
Types
image/gif
image/jpeg
image/png
But beside the obvious content-type you should also look at other headers such as:
Content-Length
The length of the response body in octets (8-bit bytes)
Example: header('Content-Length: 348');
See the function filesize
Allows the connectio to be better used.
Last-Modified
The last modified date for the requested object, in RFC 2822 format
Example: header('Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT');
See the function filemtime and date to format it into the required RFC 2822 format
Example: header('Last-Modified: '.date(DATE_RFC2822, filemtime($filename)));
You can exit the script after sending a 304 if the file modified time is the same.
status code
Example: header("HTTP/1.1 304 Not Modified");
you can exit now and not send the image one more time
For last modified time, look for this in $_SERVER
If-Modified-Since
Allows a 304 Not Modified to be returned if content is unchanged
Example: If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
Is in $_SERVER with the key http_if_modified_since
List of HTTP header responses
To achieve something like this, your script will need to :
send the right headers, which depend on the type of the image : image/gif, image/png, image/jpeg, ...
send the data of the image
making sure nothing else is sent (no white space, no nothing)
This is done with the header function, with some code like this :
header("Content-type: image/gif");
Or
header("Content-type: image/jpeg");
or whatever, depending on the type of the image.
To send the data of the image, you can use the readfile function :
Reads a file and writes it to the
output buffer.
This way, in one function, you both read the file, and output its content.
As a sidenote :
you must put some security in place, to ensure users can't request anything they want via your script : you must make sure it only serves images, from the directory you expect ; nothing like serveImage.php?file=/etc/passwd should be OK, for instance.
If you're just willing to get the number of times a file was loaded each day, parsing Apache's log file might be a good idea (via a batch run by cron each day at 00:05, that parses the log of the day before, for instance) ; you won't have real-time statistics, but it will require less resources on your server (no PHP to serve static files)
I use the "passthru" function to call "cat" command, like this:
header('Content-type: image/jpeg');
passthru('cat /path/to/image/file.jpg');
Works on Linux. Saves resources.
You must set the content type:
header("Content-type: image/jpeg");
Then you load the image and output it like this:
$image=imagecreatefromjpeg($_GET['img']);
imagejpeg($image);
Instead of changing the direct image url in the HTML, you can put a line in the Apache configuration or .htaccess to rewrite all the requests of images in a directory to a php script. Then in that script you can make use of the request headers and the $_server array to process the request and serve the file.
First in your .htaccess:
RewriteRule ^(.*)\.jpg$ serve.php [NC]
RewriteRule ^(.*)\.jpeg$ serve.php [NC]
RewriteRule ^(.*)\.png$ serve.php [NC]
RewriteRule ^(.*)\.gif$ serve.php [NC]
RewriteRule ^(.*)\.bmp$ serve.php [NC]
The script serve.php must be in the same directory as .htaccess. You will probably write something like this:
<?php
$filepath=$_SERVER['REQUEST_URI'];
$filepath='.'.$filepath;
if (file_exists($filepath))
{
touch($filepath,filemtime($filepath),time()); // this will just record the time of access in file inode. you can write your own code to do whatever
$path_parts=pathinfo($filepath);
switch(strtolower($path_parts['extension']))
{
case "gif":
header("Content-type: image/gif");
break;
case "jpg":
case "jpeg":
header("Content-type: image/jpeg");
break;
case "png":
header("Content-type: image/png");
break;
case "bmp":
header("Content-type: image/bmp");
break;
}
header("Accept-Ranges: bytes");
header('Content-Length: ' . filesize($filepath));
header("Last-Modified: Fri, 03 Mar 2004 06:32:31 GMT");
readfile($filepath);
}
else
{
header( "HTTP/1.0 404 Not Found");
header("Content-type: image/jpeg");
header('Content-Length: ' . filesize("404_files.jpg"));
header("Accept-Ranges: bytes");
header("Last-Modified: Fri, 03 Mar 2004 06:32:31 GMT");
readfile("404_files.jpg");
}
/*
By Samer Mhana
www.dorar-aliraq.net
*/
?>
(This script can be improved!)
Also, if you want to the user to see a real filename instead of your scriptname when the user RMC's on the image and selects "Save As", you'll need to also set this header:
header('Content-Disposition: filename=$filename');
You're probably better off examining your server access logs for this. Running all images through php might put a bit of load on your server.
I serve my images with readfile as well, but I have gone the extra mile both for security and extra functionality.
I have a database set up which stores the image id, its dimensions and file extension. This also means that images need to be uploaded (allowing optional resizing), so I only use the system for content and not images needed for the website itself (like backgrounds or sprites).
It also does a very good job at making sure you can only request images.
So, for serving the simplified workflow would be like this (cannot post production code here):
1) get the ID of the requested image
2) Look it up in the database
3) Throw headers based on the extension ("jpg" gets remapped to "jpeg" on upload)
4) readfile("/images/$id.$extension");
5) Optionally, protect /images/ dir so it cannot be indexed (not a problem in my own system as it maps URLS like /image/view/11 to something like /index.php?module=image&action=view&id=11)
There are a lot of good answers above, but none of them provide working code that you can use in your PHP app. I've set mine up so that I lookup the name of the image in a database table based off a different identifier. The client never sets the name of the file to download as this is a security risk.
Once the image name is found, I explode it to obtain the extension. This is important to know what type of header to serve based off the image type (i.e. png, jpg, jpeg, gif, etc.). I use a switch to do this for security reasons and to convert jpg -> jpeg for the proper header name. I've included a few additional headers in my code that ensure the file is not cached, that revalidation is required, to change the name (otherwise it will be the name of the script that is called), and finally to read the file from the server and transmit it.
I like this method since it never exposes the directory or actual file name. Be sure you authenticate the user before running the script if you are trying to do this securely.
$temp = explode('.', $image_filename);
$extension = end($temp); // jpg, jpeg, gif, png - add other flavors based off your use case
switch ($extension) {
case "jpg":
header('Content-type: image/jpeg');
break;
case "jpeg":
case "gif":
case "png":
header('Content-type: image/'.$extension);
break;
default:
die; // avoid security issues with prohibited extensions
}
header('Content-Disposition: filename=photo.'.$extension);
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
readfile('../SECURE_DIRECTORY/'.$image_filename);
PHP 8 lets you use the match feature, which will further optimize the code by getting rid of the switch and ugly looking nested cases.