Automatically copy images (png, jpeg, gif) from remote server (http) to your local server using PHP

This entry is part 1 of 2 in the series PHP Image Manipulation

So, have you ever found the necessity to copy remote images to your local server using http URLs? This is pretty easy using PHP and PHP GD. If you have both of them installed (in most of the servers they come installed) then you can do that in no time. But then comes the security issue. Copying anything from remote server to your local server can expose your server to many vulnerabilities. Just like, recently our site got hacked due to TimThumb vulnerability. So, we decided to put together a working code which you can use on your projects to copy any valid images from remote to local server. The copying process is pretty much automatic given our code. We have wrapped them in a function and it is available for you to download as well. We shall now discuss the working of the method. But before that:

Demo[download id=”14″ format=”1″]

PS: We have integrated this tutorial into a series. You will now be able to find more articles on PHP GD under this series.

#1: Understanding the concept:

While creating such automated script, we should think of a few things. Or in other words, while creating the function, we should think of the parameters first.

  • $img_url: The URL of the image. We should accept URLs starting with http:// or https:// and ending with .png, .jpg, .jpeg or .gif. Others should be rejected.
  • $store_dir: The directory where we should store the images.
  • $store_dir_type: Should specify whether the directory path is relative to the script or is absolute.
  • $overwrite: In case of duplicate file name, should it overwrite?
  • $pref: Internally used for dynamic renaming of duplicate files using tail recursion  (a function, directly returns the same function call with some different arguments).
  • $debug: A little bit of debug information is always fun, and well, informative.

So, keeping these things, we write in the skeleton of our function.

/**
 * Fetch JPEG or PNG or GIF Image
 *
 * A custom function in PHP which lets you fetch jpeg or png images from remote server to your local server
 * Can also prevent duplicate by appending an increasing _xxxx to the filename. You can also overwrite it.
 *
 * Also gives a debug mode to check where the problem is, if this is not working for you.
 *
 * @author Swashata <swashata ~[at]~ intechgrity ~[dot]~ com>
 * @copyright Do what ever you wish - I like GPL :) (& love tux ;))
 * @link http://www.intechgrity.com/?p=808
 *
 * @param string $img_url The URL of the image. Should start with http or https followed by :// and end with .png or .jpeg or .jpg or .gif. Else it will not pass the validation
 * @param string $store_dir The directory where you would like to store the images.
 * @param string $store_dir_type The path type of the directory. 'relative' for the location in relation with the executing script location or 'absolute'
 * @param bool $overwrite Set to true to overwrite, false to create a new image with different name
 * @param bool|int $pref internally used to prefix the extension in case of duplicate file name. Used by the trailing recursion call
 * @param bool $debug Set to true for enable debugging and print some useful messages.
 * @return string the location of the image (either relative with the current script or abosule depending on $store_dir_type)
 */
function itg_fetch_image($img_url, $store_dir = 'image', $store_dir_type = 'relative', $overwrite = false, $pref = false, $debug = false) {
    //code
}

#2: Verifying the image URL:

This should be done before anything else. What we do is:

  • Check if the URL begins with http:// or https://. Reject otherwise.
  • Check if the URL ends with a valid extension (.gif, .jpeg, .jpg, .png). Reject otherwise.

We shall use PHP’s built in preg_match to search using our regular expression. The code snippet for validating the image URL is this:

    if(preg_match('/https?:\/\/.*\.png$/i', $img_url)) {
        $img_type = 'png';
    }
    else if(preg_match('/https?:\/\/.*\.(jpg|jpeg)$/i', $img_url)) {
        $img_type = 'jpg';
    }
    else if(preg_match('/https?:\/\/.*\.gif$/i', $img_url)) {
        $img_type = 'gif';
    }
    else {
        if(true == $debug)
            echo 'Invalid image URL';
        return ''; //possible error on the image URL
    }

#3: Handling duplicate files:

As mentioned before, we should handle duplicate files properly. The strategy is:

  • Somehow use smart renaming by appending _xxx (xxx is a counter) after the image name and before the extension. We shall increase the counter unless a valid non-existing filename is found.
  • Or, delete the old file altogether if overwriting is set to true.

And here is the code snippet which does the trick:

    //first get the base name of the image
    $i_name = explode('.', basename($img_url));
    $i_name = $i_name[0];

    $dir_name = (($store_dir_type == 'relative')? './' : '') . rtrim($store_dir, '/') . '/';

    //create the directory if not present
    if(!file_exists($dir_name))
        mkdir($dir_name, 0777, true);

    //calculate the destination image path
    $i_dest = $dir_name . $i_name . (($pref === false)? '' : '_' . $pref) . '.' . $img_type;

    //lets see if the path exists already
    if(file_exists($i_dest)) {
        $pref = (int) $pref;

        //modify the file name, do not overwrite
        if(false == $overwrite)
            return itg_fetch_image($img_url, $store_dir, $store_dir_type, $overwrite, ++$pref, $debug);
        //delete & overwrite
        else
            unlink ($i_dest);
    }

Note that it intelligently uses tail recursion for smart renaming. It simply returns the same function by incrementing the value of $pref variable by 1.

#4: Fetching the image from the server:

Now comes the real thing. We will now see how we can fetch the image from the remove server using some built in PHP GD functions. Note that for all these to work, you will need fopen wrapper enabled, which is enabled in most of the servers. If not, then you can edit your php.ini file and set it to true. Check here for more information. So, here is the code snippet which does the magic:

    //first check if the image is fetchable
    $img_info = @getimagesize($img_url);

    //is it a valid image?
    if(false == $img_info || !isset($img_info[2]) || !($img_info[2] == IMAGETYPE_JPEG || $img_info[2] == IMAGETYPE_PNG || $img_info[2] == IMAGETYPE_JPEG2000 || $img_info[2] == IMAGETYPE_GIF)) {
        if(true == $debug)
            echo 'The image doesn\'t seem to exist in the remote server';
        return ''; //return empty string
    }

    //now try to create the image
    if($img_type == 'jpg') {
        $m_img = @imagecreatefromjpeg($img_url);
    } else if($img_type == 'png') {
        $m_img = @imagecreatefrompng($img_url);
        @imagealphablending($m_img, false);
        @imagesavealpha($m_img, true);
    } else if($img_type == 'gif') {
        $m_img = @imagecreatefromgif($img_url);
    } else {
        $m_img = FALSE;
    }

    //was the attempt successful?
    if(FALSE === $m_img) {
        if(true == $debug)
            echo 'Can not create image from the URL';
        return '';
    }

#5: Saving the image locally on disk:

Now finally we would save the image locally on our disk. The snippet is:

    //now attempt to save the file on local server
    if($img_type == 'jpg') {
        if(imagejpeg($m_img, $i_dest, 100))
            return $i_dest;
        else
            return '';
    } else if($img_type == 'png') {
        if(imagepng($m_img, $i_dest, 0))
            return $i_dest;
        else
            return '';
    } else if($img_type == 'gif') {
        if(imagegif($m_img, $i_dest))
            return $i_dest;
        else
            return '';
    }

Note that you can edit the third variable of imagejpeg and imagepng to set the quality. More information can be found here or here.

#6: The complete code:

So putting together everything, here is the complete code:

/**
 * Fetch JPEG or PNG or GIF Image
 *
 * A custom function in PHP which lets you fetch jpeg or png images from remote server to your local server
 * Can also prevent duplicate by appending an increasing _xxxx to the filename. You can also overwrite it.
 *
 * Also gives a debug mode to check where the problem is, if this is not working for you.
 *
 * @author Swashata <swashata ~[at]~ intechgrity ~[dot]~ com>
 * @copyright Do what ever you wish - I like GPL :) (& love tux ;))
 * @link http://www.intechgrity.com/?p=808
 *
 * @param string $img_url The URL of the image. Should start with http or https followed by :// and end with .png or .jpeg or .jpg or .gif. Else it will not pass the validation
 * @param string $store_dir The directory where you would like to store the images.
 * @param string $store_dir_type The path type of the directory. 'relative' for the location in relation with the executing script location or 'absolute'
 * @param bool $overwrite Set to true to overwrite, false to create a new image with different name
 * @param bool|int $pref internally used to prefix the extension in case of duplicate file name. Used by the trailing recursion call
 * @param bool $debug Set to true for enable debugging and print some useful messages.
 * @return string the location of the image (either relative with the current script or abosule depending on $store_dir_type)
 */
function itg_fetch_image($img_url, $store_dir = 'image', $store_dir_type = 'relative', $overwrite = false, $pref = false, $debug = false) {
    //first get the base name of the image
    $i_name = explode('.', basename($img_url));
    $i_name = $i_name[0];

    //now try to guess the image type from the given url
    //it should end with a valid extension...
    //good for security too
    if(preg_match('/https?:\/\/.*\.png$/i', $img_url)) {
        $img_type = 'png';
    }
    else if(preg_match('/https?:\/\/.*\.(jpg|jpeg)$/i', $img_url)) {
        $img_type = 'jpg';
    }
    else if(preg_match('/https?:\/\/.*\.gif$/i', $img_url)) {
        $img_type = 'gif';
    }
    else {
        if(true == $debug)
            echo 'Invalid image URL';
        return ''; //possible error on the image URL
    }

    $dir_name = (($store_dir_type == 'relative')? './' : '') . rtrim($store_dir, '/') . '/';

    //create the directory if not present
    if(!file_exists($dir_name))
        mkdir($dir_name, 0777, true);

    //calculate the destination image path
    $i_dest = $dir_name . $i_name . (($pref === false)? '' : '_' . $pref) . '.' . $img_type;

    //lets see if the path exists already
    if(file_exists($i_dest)) {
        $pref = (int) $pref;

        //modify the file name, do not overwrite
        if(false == $overwrite)
            return itg_fetch_image($img_url, $store_dir, $store_dir_type, $overwrite, ++$pref, $debug);
        //delete & overwrite
        else
            unlink ($i_dest);
    }

    //first check if the image is fetchable
    $img_info = @getimagesize($img_url);

    //is it a valid image?
    if(false == $img_info || !isset($img_info[2]) || !($img_info[2] == IMAGETYPE_JPEG || $img_info[2] == IMAGETYPE_PNG || $img_info[2] == IMAGETYPE_JPEG2000 || $img_info[2] == IMAGETYPE_GIF)) {
        if(true == $debug)
            echo 'The image doesn\'t seem to exist in the remote server';
        return ''; //return empty string
    }

    //now try to create the image
    if($img_type == 'jpg') {
        $m_img = @imagecreatefromjpeg($img_url);
    } else if($img_type == 'png') {
        $m_img = @imagecreatefrompng($img_url);
        @imagealphablending($m_img, false);
        @imagesavealpha($m_img, true);
    } else if($img_type == 'gif') {
        $m_img = @imagecreatefromgif($img_url);
    } else {
        $m_img = FALSE;
    }

    //was the attempt successful?
    if(FALSE === $m_img) {
        if(true == $debug)
            echo 'Can not create image from the URL';
        return '';
    }

    //now attempt to save the file on local server
    if($img_type == 'jpg') {
        if(imagejpeg($m_img, $i_dest, 100))
            return $i_dest;
        else
            return '';
    } else if($img_type == 'png') {
        if(imagepng($m_img, $i_dest, 0))
            return $i_dest;
        else
            return '';
    } else if($img_type == 'gif') {
        if(imagegif($m_img, $i_dest))
            return $i_dest;
        else
            return '';
    }

    return '';
}

//a quick test? just uncomment the line below
//echo itg_fetch_image('http://tuxpaint.org/stamps/stamps/animals/birds/cartoon/tux.png');

You can call it as mentioned there in the comments. Play with the parameters and see what you can do. You can integrate it with PHP POST and GET operations on your live project. Do check our tutorial on PHP POST and GET for a better understanding. Also, the function returns the path of the stored image. You can change this behaviour if you like by simply altering the return statement. The usage is completely upto you.

Demo[download id=”14″ format=”1″]

So, that was all. If you have any question or need any help with it, then do drop in a comment. I will try my best to give you the support you want.

2 comments

  1. dulharjo

    can you give me the same tutorial but using javascript or jquery? thanks

    • Swashata Post author

      You can not rotate images and save it using jQuery. Some CSS3 rotation can be done though. If you are talking about something like the demo, then you check the source code

Comments are closed.