Upload Media Programmatically in WordPress

WordPress themes and plugins sometimes need to download files from third party servers, like images, documents, archives, and more. A Flickr image browser is a great example, where you wouldn’t want to hotlink to the images on Flickr, but rather download them to your own. Note that file_get_contents is not the way to go, so in this post we’ll talk about two more WordPress friendly ways.

There are two functions we could use — media_sideload_image and download_url to grab a file from a URL. The two approaches are not too different although the former uses the latter. We’ll illustrate both ways in this article so it’s up to you which one to use.

The media_sideload_image Function

As mentioned at WordCamp San Francisco 2011, this is actually one of Andrew Nacin’s favorite functions, that developers seem to be overlooking. It’s used mainly by the Press This bookmarklet but theme and plugin developers can get it to good use in their own creations.

As the inline code documentation in WordPress says, the media_sideload_image function is used to:

Download an image from the specified URL and attach it to a post.

Do note though that this function will not work everywhere since it’s defined in the wp-admin/includes directory so you’re not allowed to use it outside of the admin dashboard without including the media.php file explicitly.

It’s up to you what triggers this function but for learning purposes we’ll hook it to admin_init, get it to download the Twitter logo and display it before the rest of the admin. You should probably think of something more clever, like download when a button is pressed, etc. Here’s the code that went into our child theme’s functions.php file (could be a plugin too):

add_action( 'admin_init', function() {
    $file = media_sideload_image( 'http://a2.twimg.com/a/1318451435/phoenix/img/twitter_logo_right.png', 0 );
    echo $file;
} );

Oh yeah, that’s the PHP 5.3 way so if that doesn’t work you’ll have to add a little more to it, like this:

function my_sideload_image() {
    $file = media_sideload_image( 'http://a2.twimg.com/a/1318451435/phoenix/img/twitter_logo_right.png', 0 );
    echo $file;
}
add_action( 'admin_init', 'my_sideload_image' );

If you save that and browse to your admin dashboard, you should notice the Twitter logo appear there at the top. Not the best markup, but suitable for learning purposes. The media_sideload_image function returns an img element ready to be used in your HTML but you don’t really have control over the attachment post itself, except that second argument where you can specify which post to attach it to. In our case that’s 0.

The function works perfectly fine with images and as you’ve seen is really easy to use, straight out of the box. But sometimes you’ll want to download something more than just an image, perhaps a Zip archive or an MP3 file. That’s where media_sideload_image can’t help, so we refer to the download_url function which is actually used by media_sideload_image.

The download_url Function

The download_url function uses the WordPress HTTP class to download a file to a temporary location which can then be used with the media_handle_sideload function which handles the temporary file — copies it to the uploads folder, reads it’s meta data, creates an attachment post and gets rid of the temporary file.

The trick here is to figure out the file name since download_url returns one with a .tmp extension. We use the basename for that. Second trick is to create a file array that media_handle_sideload would understand and if it doesn’t don’t forget to unlink the temporary file since download_url won’t do that for you.

Let’s use the same hook and just modify the code. Most of it is taken from the media_sideload_image function but gives you more control over the attachment post which you can retrieve the url for with wp_get_attachment_url:

add_action( 'admin_init', function() {
    $url = ''; // Input a .zip URL here
    $tmp = download_url( $url );
    $file_array = array(
        'name' => basename( $url ),
        'tmp_name' => $tmp
    );

    // Check for download errors
    if ( is_wp_error( $tmp ) ) {
        @unlink( $file_array[ 'tmp_name' ] );
        return $tmp;
    }

    $id = media_handle_sideload( $file_array, 0 );
    // Check for handle sideload errors.
    if ( is_wp_error( $id ) ) {
        @unlink( $file_array['tmp_name'] );
        return $id;
    }

    $attachment_url = wp_get_attachment_url( $id );
    // Do whatever you have to here
} );

So as you can see, we’re using the download_url function to grab the file, we then create a $file_array which is then used media_handle_sideload. Again, we’re giving it 0 as the post ID so that it doesn’t get attached to any. Finally we’ve got an $id and an $attachment_url that could be used for whatever purpose you might think of.

Don’t miss the checks for is_wp_error, those are quite important since they get rid of the temporary file when things go wrong and return the WP_Error object for further investigation if needed.

Well, that’s all there really is to it. Two WordPress functions added to your arsenal. Do keep in mind though that this might not work on some server setups where requests to third-party servers are denied, while the majority will work just fine. Thanks for stopping by and subscribing ;)