Dynamically renaming jpeg files as they're being displayed to users - php

I'm trying to dynamically rename a jpeg image file as it's displayed to the user.
I have a file image_generate.php with the following code:
$file = $_GET['file'];
$imagepath = "path/to/image.jpg";
$image = imagecreatefromjpeg($imagepath);
header('Content-Type: image/jpeg');
$filename = "[site.com]_some_image_name_here";
header('Content-Disposition: attachment; filename="' . $filename . '.jpg"');
imagejpeg($image, NULL, 100);
imagedestroy($image); // Free up memory
And it's called by an html image tag like so:
<img src="image_generate.php?file=imagenamehere" />
So far, the output has the following results:
When I right-click on the image, and click "View Image," a download dialogue pops up and asks if I'd like to save the file "[site.com]_some_image_name_here.jpg" (I want this to happen)
If I right-click on the image, and click "Save Image As," or "Save Image," the filename that's to be saved shows up as the original filename (whatever the variable $file was able to fetch).
How can I fix the second part? I'd like to modify the filename of the image even when the user clicks "Save Image As" or "Save Image."
I DID try to change the code to this:
...
$rename = $filename . ".jpg";
imagejpeg($image, $rename, 100);
imagedestroy($image); // Free up memory
But the image fails to show up on the page with the html tag (shows up as a broken image).
I'm not very familiar with html headers and Content-Dispositions. I'm guessing there's an error in there somewhere..?
Any ideas?
Thank you for reading!
Edit: .htaccess below
<IfModule mod_rewrite.c>
RewriteEngine On
# Sends image download request to image_generate.php for parsing
RewriteRule ^path/from/img/tag/downloads/(.*).jpg$ path/to/image_generate.php?file=$1 [NC,L]
RewriteRule ^path/to/image_generate.php$ /home/path/to/image_generate.php [NC,L]
</IfModule>

Different browsers will have different behaviors.
For example, Internet Explorer doesn't try to fetch any header information before displaying the save file dialog when doing a Right Click > Save Image As (It automatically assumes you want to save the result of the request, regardless of what the result is). Neither does Firefox. Therefore, for IE & Firefox, it will be impossible to specify the filename by specifying it in the headers.
A more elegant and UA-compatible way of doing this would be by using Apache's mod_rewrite to do URL rewriting. URL rewriting allows you to reroute a request made to a URL towards another.
To set this up, you need to create a .htaccess file in the directory where image_generate.php is located containing the following:
RewriteEngine On
RewriteRule ^generated/([a-z0-9-]+)\.jpg$ generated_image.php?file=$1 [L]
Then, simply modify your HTML to point to your rewritten URL.
<img src="generated/imagenamehere.jpg" />
This also have the advantage of making your image generation completely seamless to the user (URL wise, it looks simply like a static image).
More information about mod_rewrite can be found here:
mod_rewrite - Apache HTTP Server
mod_rewrite Cheat Sheet (V2) - Added Bytes by Dave Child

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");
}
?>

Moving images from doc root for added security

My site uses bookmarklets to gather data from external sites, kinda like Pinterest. I'm concerned about security and want to move the images the bookmarklet gathers from the doc root up one level. My script has some hefty security checks in place, but I want to add this as a last line of defense.
How do I access my images within my script? Obviously using ../userimages/id/image.jpg wont work. I'm using Apache.
Thanks!
Proxy the image
You would use a proxy script to feed the images through like the following example:
// open the file in a binary mode
$name = '../userimages/id/image.jpg';
$fp = fopen($name, 'rb');
// send the right headers
header("Content-Type: image/png");
header("Content-Length: " . filesize($name));
// you may like to set some cache headers here
// dump the picture and stop the script
fpassthru($fp);
exit;
This example is from the PHP manuals fpassthru() page. You would save this script somewhere in your servers document root/httpdocs folder.
"Spoofing" the URL to the image
The easiest way to give the PHP file the appearance of being an image file to a user/browser is to use Apaches mod_rewrite. Usually I use a URL structure something like this:
http://www.example.org/image-id/image.png
Where image-id is the unique identifier for that particular image. This way the file has the correct extensions of an image instead of .php.

How do I set the default 'save image as' name for an image generated in PHP?

I have a page in my site, displaying some images that are produced my PHP. When I right click on an image and click Save Image As I get as default name the name of the php file used for generating the image.
This is for example the html for the image :
<img src="picture_generator.php?image_id=5&extension=.png">
and the name I get is:
picture_generator.php.png
Is there a way to set this name to a default one?
Thanks in advance
You can provide it in the Content-Disposition HTTP header:
header('Content-Type: image/png');
header('Content-Disposition: inline; filename="' . $filename . '"');
However, some browsers (namely Internet Explorer) are likely to ignore this header. The most bullet-proof solution is to forge the URL and make the browser believe it's downloading a static file like /images/5/foo.png while the actual path behind the scenes is /picture_generator.php?image_id=5&extension=.png. This can be accomplished by some web server modules like Apache's mod_rewrite.
You can try to set the file name using the HTTP headers but not all browsers respect that.
The simplest trick is to extend the URL so that the last part contains the desired file name:
<img src="picture_generator.php/desiredfilename.jpg?image_id=5&extension=.png&name=desiredfilename.jpg">
Note I also added the file name at the end of the query string (the name doesn't really matter) as some browsers use that part.
Depending on your server configuration this will immediately work without any special configuration (no mod_rewrite or anything like that). You can check if it works on your server by simply appending "/foo" to any PHP-URL on your site. If you see the output of your PHP, all is good. If you see a 404 error then your server configuration can't deal with such URLs.
In your picture_generator.php file you need to add a header with the name. such as
header("Content-Disposition: attachment; filename=\"myfile.png\"");

View images in browser with no extension

I have a directory in my web app where I want to keep all the user profile pictures. Users can upload all types of images (png, jpg, gif). However, I want the image URL to be friendly, eg. http://example.com/static/picture/300-username, where the file is 300-username, but with no extension. I thought of removing the extension when the user uploads, and with a PHP controller, add a:
header('Content-Type: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png');
readfile('http://example.com/static/picture/300-username');
This has worked well. I was wondering however, if this can be done by placing an .htaccess file in the picture directory, with some sort of Header, that whatever file is read in this directory, will display as picture. Is this possible?
Don’t remove the extension, just offer extensionless URIs like /static/picture/300-username. Then add a line to your .htaccess:
Options +MultiViews
Apache will set the correct content type for you. See the documentation for Content Negotiation.
You can't specify multiple Content-Type like that.. You need to pass the correct header for each image served:
<?php
$user = '300-username';
$picture = 'http://example.com/static/picture/' . $user;
$info = getimagesize($picture);
$fp = fopen($picture, "rb");
if ($info && $fp) {
header("Content-type: {$info['mime']}");
fpassthru($fp);
exit;
} else {
// error
}
?>
(Code adapted from PHP's getimagesize() documentation.)
First your path need to be physically existent, else I don't think Apache can do anything for you.
However, here may be a workaround using a RewriteRule, keep the extension (by the way, I think it's bad practice to remove extension of a file, especially when they are not plain text files), and accept a requested file like 300-username to rewrite to 300-username.jpg.

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.

Categories