Mod Rewrite rule for images - php

I want to generate various image dimensions with PHP on the fly without saving all variations.
Now I upload an image and generate various dimensions and save all those files on the server. The path to the images is as follows:
/20151217/124043_205037_x.jpg
The first part is the folder
The second part is the file name.
The 3rd part is the _x, _s, _l flag (original,small,large etc)
I want to call the PHP script and return the image like:
/imageManipulationScript.php?folder=20151217&file=124043_205037&flag=x
So I want to make a mod_rewrite rule to call:
<img src="https://somecdn.com/20151217/124043_205037_x.jpg">
and server will run this:
/imageManipulationScript.php?folder=20151217&file=124043_205037&flag=x
I will call this img src through cdn so the image will be cached and executed only once per image/dimension.
I don't want to change the current path and structure of the images because then I'll have to change the implementation on my site
Any thoughts?

You might want to tweak the 3 regex's to fit your allowed characters but something along these lines should work.
RewriteEngine on
RewriteRule ^([A-Za-z0-9-]+)/([A-Za-z0-9-]+)_([A-Za-z0-9-]+).jpg$ imageManipulationScript.php?folder=$1&file=$2&flag=$3 [NC]

Related

Facebook Passing Argument to JPG Files and Custom Image Display

I've noticed in Facebook's source code, that images are links to a PHP file, safe_image.php (or rsrc.php; it changes every now and then), with the name of the selected file appended to the end, such as:
https://external-lax3-2.xx.fbcdn.net/safe_image.php?imagename1234
Or sometimes they're the usual JPEG files with a random token appended to the end:
https://scontent-lax3-2.xx.fbcdn.net/v/t1.0-9/17353408_410522555967800_2778489440067836960_n.jpg?oh=3e00f84c6767364c9304b34f8751114d&oe=5954DA1E
What, I'm wondering is how they get a custom image viewer on their website. Usually, it's just a white background, with the selected image in the top left corner. However, they have it set in the middle with a grey-ish background.
Not only that, the linked image is direct back to the viewed PHP file; how is this possible, and how do they do it?
Cheers.
EDIT: I've also noticed if you change the img src to an invalid link, it will print an error to the page:
The image " Insert image link here " cannot be displayed because it contains errors.
Jpeg files, to no ones surprise, do not take in arguments. However, PHP does. So, what is most likely Facebook did is use a rewrite rule to 'map' their .jpg?= URL to a PHP file, which can process the arguments. That PHP file then fetches the image data from a MYSQL (like) table. If you're wondering, yes you can have the .jpg file extension display in the URL, load data from PHP, and have the image display properly in browser.
This can be achieved via PHP and .htaccess.
Firstly, let's setup our .htaccess inside whatever folder we want to have our /img.jpg?= inside of:
Options +FollowSymLinks
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^img.jpg(.*)$ imageBackground.php$1 [NC]
Yay, now we are mapping our img.jpg?image=bill to imageBackground.php?image=bill.
And then we are going to write our imageBackground.php. I wrote a very simple one, but basically all your doing is setting up headers (for which image format you're using) as well as display the image data. Obviously in practical application this would be more complicated, like maybe you're dynamically grabbing image data from a database (like Facebook).
<?php
if ($_GET['image'] == "bill") {
header("Content-type: image/pjpeg");
echo file_get_contents("bill.jpg");
}
?>

Ios safari php dynamic image not displayed

I have a php script that gereates image data and uses header() to pretend to be an image. Works great everywhere, except on ipad / iphone safari.
I think because safari does not recognize .php as a valid image type. Fair enough. How do I work around this?
Are you returning the correct image mime type in your php script? E.g.
header('Content-type: image/jpeg');
Remember to put this line before you start writing out the image data.
If you are then I don't know why it wouldn't work (can you post a link to the image?) but another thing to try would be using mod rewrite in your apache .htaccess file to map a.jpg extension to your php script, so it can be accessed using a URL that looks like a regular image.
That would look something like this:
RewriteEngine On
RewriteBase /
RewriteRule ^images/([a-z0-9]+).jpg$ imagescript.php?name=$1
So accessing images/foo.jpg on your server would actually load imagescript.php.php?name=foo

Wrong Header generated by Timthumb Script

I am using the current Timthumb script to generate thumbnails of some restaurant images. I extendes the script, to be have a more effective caching.
The code can be found here on pastebin
Changes made:
Line 216 removed the security string, because I do not use the smarty caching system and this lines broke the images most of the time for me
Lines 315 - 345 use a differnet caching path
I use the following htaccess rule to avoid using timthumb and php if the file has been generated once
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI} !-s
RewriteRule ^(.*)/service/(.*)/(.*)-(.*)-(.*).jpg$ /timthumb.php?name=$1/service/$2/$3-$4-$5.jpg&w=$4&h=$5&src=http://s3-eu-west-1.amazonaws.com/$1/restaurants/$2/default.jpg [L]
Sadly I get for some pictures a corrupted image as a response.
Original
http://lieferando.de.s3.amazonaws.com/restaurants/12381/default.jpg
Created by Timthumb
e.g.: http://image.yourdelivery.de/lieferando.de/service/13003/Arman-155-100.jpg
the created image has a prepending jpg ?> and I do not know where this comes from. Anyone any clue?
I found the part, where timthumb adds those lines.
file_put_contents($tempfile4, $this->filePrependSecurityBlock . $imgType . ' ?' . '>'); //6 extra bytes, first 3 being image type
where the variable $imgType is added. I do not really understand the need of that header and why it is only in some of my pictures. But removing those lines did it for me. It seems like timthumb adds some headers to the file, so it will not be readable but by timthumb caching system itself...

Which is more efficient/faster when calling a cached image?

i made an image resizer in php. When an image is resized, it caches a new jpg file with the new dimensions. Next time you call the exact img.php?file=hello.jpg&size=400 it checks if the new jpg has already been created.
If it has NOT been created yet, it creates the file and then prints the output (cool).
If it ALREADY exists, no new file needs to be generated and instead, it just calls the already cached file.
My question is regarding the second scenario. Which of these is faster?
redirecting: header('Location: cache/hello_400.jpg');die();
grabbing data and printing the cached file: $data = file_get_contents('cache/hello_400.jpg'); header('Content-type: '.$mime);
header('Content-Length: '.strlen($data));
echo $data;
Any other ways to improve this?
If someone wants the generated code, check this out:
http://egobits.com/misc/img.phps
Thanks to all for the help!
I would opt for never printing the data to the browser. Both scenarios should throw a permanent redirect to the generated image. Except if the image doesn't exist yet, it is created before the Location header is sent.
Edit:
Just to be clear about what I mean by permanent redirect...
header('HTTP/1.1 301 Moved Permanently');
header('Location: http://path/to/image');
Maybe you could do the following:
Set some directory for these images.
Link to images in this directory (<a href="/img/resizable/hello_400.jpg>).
Set your webserver to redirect to your php script if the image doesn't exist yet. If you are on Apache, a simple .htaccess will do. In PHP, you have $_SERVER["REQUEST_URI"] from which you can tell which image you should resize.
Your script saves and echoes the image (but is called only for the first time).
This way, your get some benefits:
The image is cached (in a proxy or a browser) as any other static file.
PHP doesn't have to be called for every request just to redirect or output statical data.
You leave the implementation of If-modified-since and other cache-related headers to the webserver.
And the links look nicer :-)
Example .htaccess in your /img/resizable folder:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* /img.php [L]
Any other ways to improve this?
Yes.
There is a way to send nothing but just an HTTP header: a conditional get.
You can take a look at the similar script, http://shiftingpixel.com/2008/03/03/smart-image-resizer/, for the implementation
As a third (more powerful) option: cache the image in a binary field in a database, and query the database for it.
Implement all three solutions and benchmark them.
I'm going to guess that the first option (redirect) will be the slowest in the real world, because it requires just as much effort as the second option (file_get_contents), but involves a second request and more overhead.
If possible in your case, you can also implement a function to directly set the url of the cached image in your html like:
<img src="<?php getImageUrl('hello.jpg', 400); ?>" />
getImageUrl() will return the url of the cached image if it exists else it will return the url to dynamically generate the resized image.

Best way to cache resized images using PHP and MySQL

What would be the best practice way to handle the caching of images using PHP.
The filename is currently stored in a MySQL database which is renamed to a GUID on upload, along with the original filename and alt tag.
When the image is put into the HTML pages it is done so using a url such as '/images/get/200x200/{guid}.jpg which is rewritten to a php script. This allows my designers to specify (roughly - the source image maybe smaller) the file size.
The php script then creates a hash of the size (200x200 in the url) and the GUID filename and if the file has been generated before (file with the name of the hash exists in TMP directory) sends the file from the application TMP directory. If the hashed filename does not exist, then it is created, written to disk and served up in the same manner,
Is this efficient as it could be? (It also supports watermarking the images and the watermarking settings are stored in the hash as well, but thats out of scope for this.)
I would do it in a different manner.
Problems:
1. Having PHP serve the files out is less efficient than it could be.
2. PHP has to check the existence of files every time an image is requested
3. Apache is far better at this than PHP will ever be.
There are a few solutions here.
You can use mod_rewrite on Apache. It's possible to use mod_rewrite to test to see if a file exists, and if so, serve that file instead. This bypasses PHP entirely, and makes things far faster. The real way to do this, though, would be to generate a specific URL schema that should always exist, and then redirect to PHP if not.
For example:
RewriteCond %{REQUEST_URI} ^/images/cached/
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
RewriteRule (.*) /images/generate.php?$1 [L]
So if a client requests /images/cached/<something> and that file doesn't exist already, Apache will redirect the request to /images/generate.php?/images/cached/<something>. This script can then generate the image, write it to the cache, and then send it to the client. In the future, the PHP script is never called except for new images.
Use caching. As another poster said, use things like mod_expires, Last-Modified headers, etc. to respond to conditional GET requests. If the client doesn't have to re-request images, page loads will speed dramatically, and load on the server will decrease.
For cases where you do have to send an image from PHP, you can use mod_xsendfile to do it with less overhead. See the excellent blog post from Arnold Daniels on the issue, but note that his example is for downloads. To serve images inline, take out the Content-Disposition header (the third header() call).
Hope this helps - more after my migraine clears up.
There is two typos in Dan Udey's rewrite example (and I can't comment on it), it should rather be :
RewriteCond %{REQUEST_URI} ^/images/cached/
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
RewriteRule (.*) /images/generate.php?$1 [L]
Regards.
One note worth adding is to make sure you're code does not generate "unauthorized" sizes of these images.
So the following URL will create a 200x200 version of image 1234 if one doesn't already exist. I'd highly suggest you make sure that the requested URL contains image dimensions you support.
/images/get/200x200/1234.jpg
A malicious person could start requesting random URLs, always altering the height & width of the image. This would cause your server some serious issues b/c it will be sitting there, essentially under attack, generating images of sizes you do not support.
/images/get/0x1/1234.jpg
/images/get/0x2/1234.jpg
...
/images/get/0x9999999/1234.jpg
/images/get/1x1/1234.jpg
...
etc
Here's a random snip of code illustrating this:
<?php
$pathOnDisk = getImageDiskPath($_SERVER['REQUEST_URI']);
if(file_exists($pathOnDisk)) {
// send header with image mime type
echo file_get_contents($pathOnDisk);
exit;
} else {
$matches = array();
$ok = preg_match(
'/\/images\/get\/(\d+)x(\d+)\/(\w+)\.jpg/',
$_SERVER['REQUEST_URI'], $matches);
if(! $ok) {
// invalid url
handleInvalidRequest();
} else {
list(, $width, $height, $guid) = $matches;
// you should do this!
if(isSupportedSize($width, $height)) {
// size is supported. all good
// generate the resized image, save it & output it
} else {
// invalid size requested!!!
handleInvalidRequest();
}
}
}
// snip
function handleInvalidRequest() {
// do something w/ invalid request
// show a default graphic, log it etc
}
?>
Seems great post, but my problem still remains unsolved. I dont have access to htaccess in my host provider, so no question of apache tweaking. Is there really a way to set cace-control header for images?
Your approach seems quite reasonable - I would add that some mechanism should be put into place to check that the date the cached version was generated was after the last modified timestamp of the original (source) image file and if not regenerate the cached/resized version. This will ensure that if an image is changed by the designers the cache will be updated appropriately.
That sounds like a solid way to do it. The next step may be to go beyond PHP/MySQL.
Perhaps, tweak your headers:
If you're using PHP to send MIME types, you might also use 'Keep-alive' and 'Cache-control' headers to extend the life of your images on the server and take some of the load off of PHP/MySQL.
Also, consider apache plugin(s) for caching as well. Like mod_expires.
Oh, one more thing, how much control do you have over your server? Should we limit this conversation to just PHP/MySQL?
I've managed to do this simply using a redirect header in PHP:
if (!file_exists($filename)) {
// *** Insert code that generates image ***
// Content type
header('Content-type: image/jpeg');
// Output
readfile($filename);
} else {
// Redirect
$host = $_SERVER['HTTP_HOST'];
$uri = rtrim(dirname($_SERVER['PHP_SELF']), '/\\');
$extra = $filename;
header("Location: http://$host$uri/$extra");
}
Instead of keeping the file address in the db I prefer adding a random number to the file name whenever the user logs in. Something like this for user 1234: image/picture_1234.png?rnd=6534122341
If the user submits a new picture during the session I just refresh the random number.
GUID tackles the cache problem 100%. However it sort of makes it harder to keep track of the picture files. With this method there is a chance the user might see the same picture again at a future login. However the odds are low if you generate your random number from a billion numbers.
phpThumb is a framework that generates resized images/thumbnails on the fly. It also implements caching and it's very easy to implement.
The code to resize an image is:
<img src="/phpThumb.php?src=/path/to/image.jpg&w=200&h=200" alt="thumbnail"/>
will give you a thumbnail of 200 x 200;
It also supports watermarking.
Check it out at:
http://phpthumb.sourceforge.net/

Categories