WordPress Upload Attachment

Uploading an Image to WordPress

An often requested part of WordPress is the ability to upload a file attachment or upload an image. This tutorial is a continuation of the WordPress Tutorial: Custom Profile Page. Using the WordPress function media_handle_upload() we will be uploading an image for our custom profile. More then that, we will be uploading an image in WordPress and then deleting the old one using wp_delete_attachment() and, lastly, we will be displaying it using wp_get_attachment_image().

Download the full Custom Profile with Image Upload files

This is what we are adding to the custom profile form:
Wordpress Upload Profile Image Finished

The Profile Page

Starting with the form page we will be adding a link to our functions

require_once (TEMPLATEPATH . '/Profile/profile_image.php');

We add more data to use with our functions – specifically we get the current attachment assigned to the user.

$profile_image = get_user_meta($user_id, 'profile_image');
$profile_image = $profile_image[0];

Then a simple check that calls the functions we added:

if ( isset( $_POST['html-upload'] ) && !empty( $_FILES ) ) {

We feed this function our simple page refresh which is $redirect, followed by the ID of the user for use in the function. $profile_image is the ID of the attachment file we will be deleting when the new file is uploaded.

The last thing we need to do is add a display of the image and the upload form:

    <?php profile_image_display("medium",$profile_image); ?>
<!-- The Image Upload Form -->
    <ul id="image-upload">
    <form class="image-upload" id="file-form" enctype="multipart/form-data" action="<?php echo $_SERVER['REQUEST_URI']; ?>" method="POST">  
        <li id="async-upload-wrap">  
        <label for="async-upload">Upload</label>  
        <input type="file" id="async-upload" name="async-upload"> <input type="submit" value="Upload" name="html-upload">  
        <input type="hidden" name="post_id" id="post_id" value="1199" />  
        <?php wp_nonce_field('client-file-upload'); ?>  
        <input type="hidden" name="redirect_to" value="<?php echo $_SERVER['REQUEST_URI']; ?>" />  
<!-- End image upload form -->

Our profile_image_display() function is fed the ID of the image assigned to this user and gives it a size to display. The size uses the default sizes for WordPress, although you can feed it an array with height and width.

As it is a file upload the form needs the type to be multipart. WordPress uses async-upload as a key in the function media_handle_upload() and we assign it in our form to compliment that fact.

Profile Image Functions

function profile_image_upload($redirect,$user_id,$profile_image){
    $message = '';  //reset $message for safety
	require_once(ABSPATH . 'wp-admin/includes/admin.php');  
	//check if it is an image file
	if(!eregi('image/', $_FILES['async-upload']['type'])) {
          $message = "The uploaded file is not an image please upload a valid file!";
	  $message = str_replace(" ","%20",$message); 	  
	else{ //it is an image, upload it
	  $message = '';
	  //media_handle_upload() is what actually does the uploading         
         $id = media_handle_upload('async-upload',''); //post id of Client Files page  

As before we default $message to a NULL value when the function is called. We also include the admin.php, which is in the core admin files of WordPress that allows us access to the core functions for uploading attachment files. Now, I like checking my data to make sure it is of the type I desire. With this in mind I want to make sure the file being uploaded is of the type ‘image’. You can define this however you like, as PHP has several file type identifications – should you want the user to be able to upload a different type of file. To do this I use the PHP eregi() function and give an error if it is not of the file type I am looking for.

If the upload file is an image I call the media_handle_upload() function to add it. It returns the ID of the attachment so I make sure to take advantage of this by assigning the returned value to a variable. However, the media_handle_upload() requires 2 variables: the data to upload, and the POST to attach it to. But..we don’t want to attach it to a post.

Thankfully, the function doesn’t actually check that the post value is actually a value! We take advantage of this by providing a NULL value which then forces the function not to assign the attachment uploaded to a post. It’s a neat trick and works wonders…until WordPress decides to enforce a value. Even then it is a simple matter to assign a default post to the attachment and create a function that removes that link. Should the need arise I’ll update this tutorial then.

The last step is to scrape the files to the side of the road and push them down the sewer drain. In otherwords, we unset anything in the $_FILES array as garbage cleanup.

	    if($message=='') { 
		 //delete the old image
		    wp_delete_attachment( $profile_image );
		 //add, or update the new one
		 update_user_meta($user_id, 'profile_image', $id, false);
		 $message = "Image uploaded";
		 $message = str_replace(" ","%20",$message);
	catch (Exception $e) {
		$message = 'Caught%20exception:%20'.  $e->getMessage(). "n";
		$message = str_replace(" ","%20",$message);
	//redirect to refresh the page
	wp_redirect( home_url().$redirect.'&update='.$message );

Here is the meat of what we actually do with the data. We start by checking if an image is already assigned to the user and if it is we delete it. Then we add/update the user metadata we want to hold the attachment ID we set above with the new value. As before, we use update_user_meta(). We create/update a new meta field and call it ‘profile_image’ and insert the attachment ID. And we also like the user to stay informed so we let them know by updating our $message.

The last detail, as was done with the custom Profile Page tutorial, is redirect back to the form page and refresh it by, basically, opening it all over again in the browser.

That’s it for the key function to upload a file attachment in WordPress.

Displaying the Image

//display the image if one exists
/*  $size accepts
 *  thumbnail, medium, large or full
 *  or 2-dimensional array e.g  $size = array(32,32);
function profile_image_display($size,$img_id){
	     echo wp_get_attachment_image($img_id,$size);

This last function we call on our form page to display the image. What we do for that is simply take the user meta with the ID of the image and, if it exists, display the image with wp_get_attachment_image(). $img_id is the attachment ID and $size is just that – the size of the image.

Add Style

/* ----------- stylized ----------- */
#stylized li{
#stylized label{
#stylized input, select{
padding:4px 2px;
margin:2px 0 20px 10px;

#stylized li:last-child input {
	-moz-box-shadow:inset 0px 1px 0px 0px #ffffff;
	-webkit-box-shadow:inset 0px 1px 0px 0px #ffffff;
	box-shadow:inset 0px 1px 0px 0px #ffffff;
	background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #ededed), color-stop(1, #dfdfdf) );
	background:-moz-linear-gradient( center top, #ededed 5%, #dfdfdf 100% );
	filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ededed', endColorstr='#dfdfdf');
	border:1px solid #dcdcdc;
	padding:6px 24px;
	text-shadow:1px 1px 0px #ffffff;
#stylized li:last-child input:hover {
	background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #dfdfdf), color-stop(1, #ededed) );
	background:-moz-linear-gradient( center top, #dfdfdf 5%, #ededed 100% );
	filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#dfdfdf', endColorstr='#ededed');
}#stylized li:last-child input:active {

/*---------------IMAGE UPLOAD--------------------*/
#image-upload {list-style:none;border:2px dotted #dcdcdc;width:396px;background-color:#ededed};

The WordPress Custom Profile Result

Wordpress Upload Profile Image

You can download the full Custom Profile with Image Upload files if you would like.

To learn about adding a WordPress custom profile it is best to start with WordPress Tutorial: Custom Profile Page

I hope you enjoyed these tutorials and happy coding!

Related posts:

About the Author: Neil Davidson

Neil Davidson writes articles and tutorials about web development. He is happily married with three dogs, two cats and an ominous spirit in his home.

18 Comments + Add Comment

  • Nice tutorial, but I was wondering why did you use two forms?

    • Howdy,

      I create the upload as a second form to create better control. This way you can easily create multi-file uploads without affecting user information functions.

  • Nice
    how to custom users create and share images, video and audio albums?

    • That’s the beauty of using wordpress. All you really need is the user id. That what you use to access the meta, and built in user information – as can be seen, in a simple way, on the first part of the tutorial series in the form – Part 1

      Then you simply use a built-in function like get_user_meta($user_id, ‘profile’); to get the information you need. To direct answer to your question is all you need is control of the key which is the user ID.

  • Thank you so much!

    It works like a dream. I tried to do something like this on my own, and then finally stumbled over here. I’ll definitely learned a lot from your tutorials.

  • I want to upload image but with my plugin, like in media.php. How to?
    Can you make for me? Please. You can download my plugin

  • I can’t get the second part of the tutorial work, you didn’t really specify where I’m supposed to put all that code above.

    I added it to my functions.php and it broke the site.

  • First off, great work, the profile form saved me a lot of hassle. However, I’m having issues with the avatar uploading. I’ve been playing around with this for a little, but I can’t seem to get the image to actually upload. It shows that it was “saved” in the uploads folder, however when I check the folder itself there’s nothing in there, even when I attempt to do it as an admin.

    Also, I was getting a header already sent error, but I just commented out where the header was called in the pluggable.php file

  • Well, scratch that. Apparently my wordpress installation was corrupt cause i ended up installing a fresh version and it works fine now, thanks!

    • Glad to hear all is working well!

  • Hei Neil,
    Thanks for this huge and clear post.

    I test at localhost and all works perfectly, the pictures are uploaded and Profile fields updated correctly, except for a warning.

    Warning: Cannot modify header information – headers already sent by (output started at D:\wp\wp-includes\nav-menu-template.php:240) in D:\wp\wp-includes\pluggable.php on line 876.

    I having this warning when click Upload or Update Profil buttons and require refreshing page with browser refresh button to update page and changes take effect. After refresh page the warning stay at page.

    Could you help me? I will appreciate.

    • Hello Rufino,

      This is a common problem when there is a space, or trailing spaces at the end of the functions.php file. As another comment mentions, it could also be a corrupt wordpress installation.

      I was going to create a plugin but I haven’t had time to build all the requirements to make it adaptable for general usage. This would include features such as adding form fields in the backend dynamically and building a class to auto-generate the profile form on a profile page when the user is logged in. Doing this would remove user comments like yours, as it would remove that common issue for new developers.

      Until then, I would start with the above fixes. Let me know how it goes for you and happy programming!

      • Hello Neil,
        Thanks for you quickly answer.
        I test with a new WP installation and a BLANK theme.
        Everything works well except that when click Upload or Update Profil buttons redirects to Error 404 Page not Found.
        Later check in User Profile Page and wp_admin profil page the changes are correct and the img is loaded.
        Look as a redirect problem, and this is a unknown world at present for me.

        • When wordpress installs or reinstalls pages/posts almost always have a different ID. The 404.html is due to the redirect location being incorrect. Change the redirect URL and all will be good.

          As a note to you – this tutorial is for instructional purposes and for a live website you would have a more user friendly interface if you used AJAX to update and re-load data.

          No, can’t help ya there – AJAX beyond the scope of this intermediate tutorial. Lots of resources to implement and understand AJAX on the web – Google is your friend! If you do require custom implementation or coding I do offer that at an hourly rate just shoot me an email on the contact page.

          • Hello Neil,
            You are right I correct the redirect page and now all things are working properly.
            As you said was a corrupted wp. I can´t say to readers where the problem was, i re-started with a new Wp installation and step by step made the changes, new functions, new pluggins, css changes etc. and the problem.disappear.
            Thanks for your offer, but I´m retired and my hobby, apart golf, is to learn and understand programming.
            I will follow your AJAX advice.
            One more, Thanks for your help and your great and clear tutorial.

  • Hi,

    Is there a way Ican just download your pluggiin

    I have no idea how to program a wordpress file

    Kind regards


  • Have been looking all over for this.. let me give it a try since all the comments seems positive… Thanks Neil in advance

Leave a comment