Outputting image using php - php

There are a bunch of questions of with the same title as this one.
For instance
How to output images using php?
Output as an image using PHP
How to output image and download using php
Invariably the answer is some variation of
add header('Content-type: image/jpeg'); to your code
Which is an important part of the answer, no doubt, but leaves out some vital steps I really cannot work out.
Now, there is obvious benefit to fetching an image using php when the image is situated below the root directory.
So suppose you have a web page
<?php
require_once("load_a_scripts.php");
print("lots of html");
// fetch image from below root directory and output in HTML
?>
<img id="this_one" src="" height="200" width="200" />
And you want to output the image this_one, above?
PHP can be used to fetch the required file using readfile(), but that will not be terribly useful here (at best you will get random unicode output instead of a proper image here).
The solution to prevent garbled output is to use
header('Content-type: image/jpeg');
This clearly seems inappropriate here. Not only is there output, which would make setting the header impossible, but even if this course of action is successful, the best that one can hope for is a download prompt for the image. Again, this is not desired functionality.
So some solutions suggest some external code, like "image.php" that will have its own header and will fetch the image, and so on. However, there's again some issues with this.
How will image.php know what what image it is you want?
<?php header('Content-Type:'.$photo);
if(isset($photo)&&isset($type)){
if(strlen($photo)>15||strlen($type)>6){
die();
}
$photo = strtolower($photo);
$photo = preg_replace('/[^a-z0-9 -]+/', '', $photo);
$photo = str_replace(' ', '-', $photo);
$photo = trim($photo, '-');
$type = strtolower($type);
$type = preg_replace('/[^a-z0-9 -]+/', '', $type);
$type = str_replace(' ', '-', $type);
$type = trim($type, '-');
readfile('../img/'.$user_id.$photo);
}
The parameters relating to the image name ($photo) and mime ($type) can hypothetically be sent using GET, but again, what sends these parameters, and how?
Some answers seemed to indicate that you could directly link to the external image.php as if it were an image
<img src="image.php" />
But this again raises the question about how image.php knows what the values for $photo and $type.

You have two basic options: first, using an external image-loader/handler script (e.g. <img src="image.php?i=sunshine.jpg" />); second, base64-encoding the image data (e.g. <img src="data:image/png;base64, i26dfdXAd..." />). The second option will make your HTML bloat like there's no tomorrow. So let's simply look at the image.php handler approach.
How does the handler know what image to show? It'll need a query string, e.g. image.php?i=sunshine.jpg; or image.php?i=sunshine&x=jpg; or image.php?i=sunshine&x=jpg&s=600, if you wanted to load a particular size, and so on. You will have to use that format in your HTML source, there's no script that would automagically know what image to load. Then, it's "who" (you), not "what", that sends the parameters. You'll want to make sure that the variables are properly sanitized (as your sample handler is doing); like any user-modifiable input, they shouldn't be trusted, especially anywhere near filesystem operations (directory traversal, etc.). These variables will be available as $_GET['i'], $_GET['x'], etc. to your handler script.
If you feel like URL parameters are a hassle, or just look dirty, you could always rewrite URLs. Link an image as <img src="images/sunshine.jpg" />, and have a .htaccess rewrite rule in place in the /images/ folder (containing only your script) that passes all image requests to your handler script. See the "htaccess redirect all images to handler" Q/A for an example. You could also use this approach for handling down-scaled image versions, with source URLs like <img src="images/sunshine__600.jpg" /> mapped to a rewrite rule.
On the use of readfile() (the most memory-friendly way of blurting out large chunks of data), as long as you output the header('Content-type: image/jpeg'); (or other appropriate MIME header) before your readfile() call, it will work in the desired way. That is to say, browser won't know the difference between images served directly from the filesys by the webserver, or images served from outside the webroot with a handler. It's just data with an image HTTP header.
Edit: Bonus. Coded a minimalist safe-to-deploy file-handler.
<?php
/*
* Receive image call as: <img src="img.php?i=name.jpg" />
* Output matching image file from $basepath directory
*/
$basepath = '../images/'; // Path to image folder
$mimes = [ // Allowed image types:
'jpg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'svg' => 'image/svg+xml',
];
!isset($_GET['i']) && die(); // Nothing to do here.
// Parse & Verify Extension
$ext = strtolower(pathinfo($_GET['i'], PATHINFO_EXTENSION));
!isset($mimes[$ext]) && die('Invalid filetype!'); // If MIME N/A: die!
// Parse & Sanitize Filename
$file = basename($_GET['i']);
$file = preg_replace('#[^[:alnum:] ._-]#', '', $file);
$filepath = realpath($basepath . $file);
$filepath === false && die('Invalid filename!'); // If image N/A: die!
// Output MIME header and image:
header('Content-Type:' . $mimes[$ext]);
readfile($filepath);
exit;
If you need to accommodate jpeg etc. variants, simply a) duplicate the MIME definitions in the $mimes array, or b) add an extension normalization routine. If you need to access images in subfolders, e.g. ?i=sun/shine.jpg, rework the filename parse/sanitize routine without basename. (Be sure you're safe from directory traversal!). Characters other than [:alnum:] ._-] are removed from the requested filename. Modify the regex as necessary.

Related

Saving a remote image programmatically with PHP

I am trying to migrate some content from one resources into another and need to save some images (several hundred) located at a remote resource.
Suppose I have only the URL to an image:
https://www.example.com/some_image.jpg
And I would like to save it into the filesystem using PHP.
If I were uploading the image, I essentially would do the following:
<input type="file" name="my_image" />
move_uploaded_file($_FILES['my_image']['tmp_name'], '/my_img_directory');
But since I only have the URL, I would imagine something like:
$img = 'https://www.example.com/some_image.jpg';
$file = readfile($img);
move_uploaded_file($file, '/my_img_directory');
Which of course wouldnt work since move_uploaded_file() doesn't take an output buffer as a first argument.
Essentially, I would need to get $img into the $_FILES[] array under this approach. Or may some other approach?
You can use PHP's copy function to copy remote files to a location on your server:
copy("https://example.com/some_image.jpg", "/path/to/file.jpg");
http://php.net/manual/en/function.copy.php
$image = file_get_contents('http://www.url.com/image.jpg');
file_put_contents('/images/image.jpg', $image); //Where to save the image on your server

PHP image don't upload to server

I am having a problem in getting a image uploading to the server (first I convert it to .png and resize it):
In functions.php
function imageUploadedResize($source, $target,$nWidth, $nHeight)
{
$sourceImg = Imagecreatefromstring(file_get_contents($source));
if ($sourceImg === false)
{
return false;
}
$width = imagesx($sourceImg);
$height = imagesy($sourceImg);
$targetImg = imagecreatetruecolor($nWidth, $nHeight);
imagecopyresized($targetImg, $sourceImg, 0, 0, 0, 0, $nWidth,$nHeight,$width, $height);
imagedestroy($sourceImg);
imagepng($targetImg, $target);
imagedestroy($targetImg);
}
In uploadtoServer.php
if(isset($_POST["fileToUpload"]))
{
$target_dir = "img/data/";
$fn = $_FILES['fileToUpload']['tmp_name'];
$newFileName = mysqli_insert_id($link).".png";
header('Content-type: image/png');
imageUploadedResize($fn, $target_dir.$newFileName, 45,60);
}
If I change $fn to a static image like "https://pbs.twimg.com/profile_images/54789364/JPG-logo-highres.jpg" it works so I guess I am having a problem in $fn. Any thoughts what can it be?
Part 1:
Form input
As Johnathan mentioned, you're checking isset($_POST["fileToUpload"]) when a file upload form will supply the value in the $_FILES array. So you need to change your reference from $_POST to $_FILES otherwise it will always return false.
Edit: As your comment states you are getting 100% false, you should use a more specific qualifier such as if($_FILES['fileToUpload']['error'] == 0) .
Your HTML form submitting the data needs to be enctype="multipart/form-data" otherwise your server will not collect anything file shaped from your form. HTML forms also need to be set in the POST method.
Part 2
File storage:
Your references to file system storage for the image, such as $target_dir = "img/data/"; are relative, so that means that the value of $target_dir needs to be the child of the file that is calling that script block.
It would probably help you a lot to use Absolute file system references, typically (but not exclusively) using $_SERVER['DOCUMENT_ROOT']."/img/data";
This example will store data in the www.your-web-site.com/img/data location.
Part 3
Displaying the image.
You have a header setting content type to image/png but you do not actually output the image, instead saving it to the file reference mentioned above. If you want to output the image to the browser, you need to skip saving it so:
imagepng($targetImg, NULL);
This will then output the image in $targetImg straight out to the browser. At the moment you appear to be saving it but not displaying it, despite setting the image header which means that any output to browser is expected to be an image content.
Some systems have a restriction on freshly uploaded data and prevent that data being handled too freely, to get around this it is advisable to use move_upladed_file to save the file to a "proper" destination and then access it to output to the browser.
Further Notes:
You should be checking $_FILES['fileToUpload']['error'] before acting on the file, as this will tell you if a file is present. using isset is a bad (very ambiguous) idea on this as the array can still be set, even if the file has not been uploaded.
You should use PHP error logging. You should not use error supression #.

PHP: set image as background without revealing path

The following problem I can't really wrap my mind around, so really if you guys can't be bothered to supply the entire code some tips leading in the right direction would be great!
So, I have a script where users can upload images to a server. PHP takes care of validating the file and saving it using a new filename in another folder, neither known by the client. Now, the client should be able to see the uploaded image, in html simply:
style="background-image:url('testimagegif.gif');
But preferably the client should not be able to see the path nor the file name of the image saved on the server. I know about using header('Content-type: ... for forcing the client browser to download files, but I do not see how this, nor any similar solution could be applied to this case. Same goes for readfile. If I use it the browser simply downloads the image, not placing it in the html.
You should probably be moving the files into a publicly readable folder on your webserver if you want to serve them.
Otherwise, you'll need something like readfile()
There are two options for this, you could use the data protocol, which would embed the whole image into the URL of the background ( this isn't recommended if the image is bigger than a few kb. ) or you can use a script to present the image by encoding or recording a unique key for the image, eg bg.php?id=4323-34442-3432-4532 which checks a db for the id to retrieve the file path then echoes the content with the right content type.
Some examples;
based on the Data URI wikipedia page
Data URI Method
Assuming a function like this;
function data_uri($fileID) {
$fRecord = mysql_fetch_array(
mysql_select("SELECT filePath, mimeType from fileTable WHERE fileID = " $fileID . ";")
);
$contents = file_get_contents($fRecord['filePath']);
$base64 = base64_encode($contents);
return "data:$fRecord['mimeType'];base64,$base64";
}
Then in your html/php page you'd have the following snippet
style="background-image:url('<?php echo data_uri($fileID);?>'
PHP Image Dump
Assuming a function like this;
// Given a filename and a mimetype; dump the contents to the screen
function showDocumentContent($fileID){
$fRecord = mysql_fetch_array(
mysql_select("SELECT filePath, mimeType from fileTable WHERE fileID = " $fileID . ";")
);
header( 'Content-Encoding: none', true );
header( 'Content-Type: ' . $fRecord['mimeType'], true );
echo readfile( $fRecord['filePath'] );
}
Then in your html page you'd have this;
style="background-image:url('image.php?fileID=123')
In the first case, images larger than a few KB will result in equally large HTML pages, and may not be supported in browsers consistently. In the second case, you'd effectively have created a php script that is pretending to be an image. In both cases, the real path to the binary files on your server is abstracted away by storing a mapping in a database.
If you store the paths to the files somewhere like a database or a file, you can use readfile() to output the file once you retrieve the path.
Combine that with the content-type header, and set the background-image URL to the PHP script with the correct query string like so:
style="background-image:url('script.php?img=30382');"
You must expose some path to the client, because their browser has to access the file. You can use your webserver config to serve at an indirected location, or serve the image with PHP and have the real path in a call to readfile()

PHP - send GET request and get picture in return

I need to send a GET request to my page pic.php, and I want to get a real picture in return.
For now I implemented this idea like this:
<?php
if ((isset($_GET['pic']))&&(isset($_GET['key']))){
$pic = $_GET['pic'];
$pic=stripslashes($pic);
header('Location: /content/'.$pic);
}
?>
But it's not really what I want - it redirects to image directly. What I want is to keep the same URL, but get a needed file depending on what values were submitted.
What is the best way to do that?
thx.
This example code snippet should do what you ask. I've also included code to only strip slashes if magic quotes is enabled on the server. This will make your code more portable, and compatible with future versions of PHP. I also added use of getimagesize() to detect the MIME type so that you output the proper headers for the image, and do not have to assume it is of a specific type.
<?php
if(isset($_GET['pic']))
{
//Only strip slashes if magic quotes is enabled.
$pic = (get_magic_quotes_gpc()) ? stripslashes($_GET['pic']) : $_GET['pic'];
//Change this to the correct path for your file on the server.
$pic = '/your/path/to/real/image/location/'.$pic;
//This will get info about the image, including the mime type.
//The function is called getimagesize(), which is misleading
//because it does much more than that.
$size = getimagesize($pic);
//Now that you know the mime type, include it in the header.
header('Content-type: '.$size['mime']);
//Read the image and send it directly to the output.
readfile($pic);
}
?>
I can see you doing this in two ways:
1) Return the URL to the image, and print out an image tag:
print '<img src=' . $img_url . ' />';
2) Alternatively, you could just pull the data for the image, and display it. For instance, set the header appropriately, and then just print the image data.
header("content-type: image/png");
print $img_data;
This assumes that you have the image data stored in a string $img_data. This method will also prevent you from displaying other things on the page. You can only display the image.
You can load the image, send the image headers, and display the image as such:
header('Content-Type: image/jpeg');
readfile('/path/to/content/pic.jpg');
Obviously the headers would depend on the filetype, but that's easy to make dynamic.
Not sure if I understand what you're after, but guessing that you want to load the picture in an img tag?
If I'm right you just do:
<img src=http://www.domain.com/pic.php?"<?php echo image here ?>" />
Basically you just make the source of the image the webpage you get directed to where the image is.

Is directly linking to an image okay for images uploaded to the server with a custom PHP script?

For an image file (JPEG) that has been uploaded to the server via a PHP script (to a directory such as http://www.somedomain.com/images, is it a good idea to allow the client to get the image's direct address (such as http://www.somedomain.com/images/someimage.jpg and paste it into a WYWSIWYG text editor (such as TinyMCE)?
I am wondering if there is a preferable method where the direct address is encrypted?
Please, if I should just link directly to the image, just say so.
Thanks!
Note: I have modified this question from my original. Please see revisions if you are curious, but I think I was asking the question incorrectly. My apologies to the people who already answered.
As long as you check correctly WHAT is being uploaded, it shouldn't be a problem. So please at least use getimagesize or a similar function to make sure it's an image that's being uploaded, AND make sure the extension on the file is correct so that it will never be run through the PHP interpreter - to prevent someone from uploading an image with a PHP script attached.
BTW Here's a nice whitepaper on uploads and security : http://www.scanit.be/uploads/php-file-upload.pdf
Depending on the CPU Constraints of your web-hosting service you can write a service to 'serve' the images to your users.
Here is some very BASIC code, it needs spiffing up and cleaning up for XSS/etc...
<?php
$basePath = "/path/to/my/image/store/not/web/accessible/";
$file = NULL;
if (isset($_GET['file']))
$file = $_GET['file'];
if ($file != NULL)
{
$path = $basePath . $file;
// $file needs to be checked for people
// trying to hack you, but for the sake of simplicity
// i've left it out
$mime = mime_content_type($path);
$size = filesize($path);
header("Content-Length: " . $size);
header("Content-Type: " . $mime);
header('Expires: 0');
readfile($path); // Outputs the file to the output buffer
}
?>
Obviously you can put whatever security checks in here you want. But this way your files are below the web dir, and you can apply logic to thier accesibility. This is typically used more for FILE vs. Images, but you can do the same thing here.
Images Accessed like this
http://www.mysite.com/image.php?file=hello.jpg
And you can use mod_rewrite to rewrite urls like this:
`http://www.mysite.com/images/hello.jpg
Into the first url.
I Cannot stress enough the need for further security checking in the above example, it was intended to show you how to serve a file to the user using PHP. Please don't copy & use this verbatim.
Wordpress uses direct links for images. The permalink function simply puts the image on a page along with metadata for comments, but the images' SRC attributes still link directly to the image.
why are you concerned about revealing your image location. Hotlinking?
if so you can prevent hotlinking with htaccess
http://altlab.com/htaccess_tutorial.html
Didn't you get your answer already?
Every site reveals image location to the browser. It's just the way web works.
Got any reason to "encrypt" original location?

Categories