-1

There are a bunch of questions of with the same title as this one. For instance

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.

Stumbler
  • 2,056
  • 7
  • 35
  • 61
  • To clear things up, are you trying to dynamically load an `` tag based on a GET variable, or do you want an entire php script to load based on a url, like `http://placehold.it/200x200` ? – Matthew Spence Jul 04 '20 at 14:43
  • 2
    Please, describe your issue more precisely and succinctly. – Salim Ibrohimi Jul 04 '20 at 15:14
  • @MatthewSpence I want to dynamically load an image using php. There is no point writing – Stumbler Jul 04 '20 at 16:09
  • @KunalRaut what part are you having issues with? There are so many questions of this kind on SO I was concerned about being deleted for being a duplicate. – Stumbler Jul 04 '20 at 16:12

1 Answers1

-1

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.

Markus AO
  • 4,771
  • 2
  • 18
  • 29
  • So essentially all it requires is correct alteration of the src attribute? That sounds easier than I expected – Stumbler Jul 04 '20 at 18:12
  • Yes that's correct. An `` tag with the appropriate `src` attribute pointing to the handler, and a handler script that serves the images out. – Markus AO Jul 04 '20 at 18:17
  • I've added in a fresh image handler specimen. Seems to work as expected. ^_^ – Markus AO Jul 04 '20 at 19:07