I’ve been working on a wordpress plugin to make it easy to set a banner image for any blog entry outside the post content. The current approach is flawed as it requires hacking at the form tag for the post edit page to allow file uploads, but while I look around for a better way to do that without sacrificing the simplicity of the UI, I wanted to share some digging I did to figure out how to handle file uploads with wordpress. All this data is available in the documentation, but it took quite a bit of looking to find it and there aren’t enough examples kicking around.

So without further ado…

When wordpress receives a file upload it hands it to the function wp_handle_upload. This takes two arguments:

/*
 * @param array $file Reference to a single element of $_FILES. Call the function once for each uploaded file.
 * @param array $overrides Optional. An associative array of names=>values to override default variables with extract( $overrides, EXTR_OVERWRITE ).
 */

and returns:

/*
 * @return array On success, returns an associative array of file attributes. On failure, returns $overrides['upload_error_handler'](&$file, $message ) or array( 'error'=>$message ).
 */

In practice that means a successful run will give you something like:

array (
  'file' => '/path/to/webroot/wp-content/uploads/myfile.png',
  'url' => 'http://my.host/wp-content/uploads/myfile.png',
  'type' => 'image/png',
)

It can be found in wp-admin/includes/file.php.

A simplified example of how I’m using it in my wordpress-post-banners plugin follows:

/* Define the callback that will be run when a post is saved */
add_action('save_post', 'post_banners_process_saved_post');

/**
 * Process any new file that has been uploaded for this post
 *
 * If a new file has been uploaded, we hand it to wordpress to process
 * and then store the details in a custom field.
 *
 * @param integer $post_id may be the actual ID, may be a revision ID
 * @return null
 */
function post_banners_process_saved_post($post_id) {
  /* If the file we want has been uploaded */
  if (isset($_FILES['post_banner_image'])) {
    $file = wp_handle_upload($_FILES['post_banner_image']);

    /* Check if it was an error */
    if (isset($file['error'])) {
      // handle error
    } else {
      // store the image details in a custom field
      global $wpdb;
      $wpdb->query($wpdb->prepare('REPLACE INTO ' . $wpdb->postmeta . ' (`post_id`, `meta_key`, `meta_value` ) VALUES (%s, %s, %s)',
        $post_ID, 'post_banner_image', $file['url']));
    }
  }
}

I’ll post again once the plugin settles down, but in the meantime hopefully an extra example of how to use wp_handle_upload will help someone.