WordPress is a great CMS and has a nice feature for uploading files. That is the media uploader which can be found inside the WordPress administration. In this tutorial we will not use the media uploader. We will develop our own WordPress file upload using AJAX and PHP.
The code used in this tutorial can be reused inside a theme or a plugin so feel free to experiment with it and create your own uploader. This tutorial will show you the code that can be used inside the WordPress administration or even on the front end.
There are different approaches on defining how to handle the file uploads:
Let’s assume that we have an upload form where the file will be inserted. This form will be hidden since we will create the upload via AJAX and PHP. To upload a file we will just click on an empty box that will be filled with an image or the filename. Here is our form:
The first element inside the form will be used to append upload messages such as a success message or even a failure message. Next one is the element #ibenic_file_upload and inside that element we will have the input which we will call upon clicking the #ibenic_file_upload.
The other element which is hidden is #ibenic_file_upload_preview and this element will be used to show the uploaded file. It will also have a button to delete the uploaded file.
We will add some simple styling to our form so that it can have a simple and slick look. You can change it on your own later but for now let’s just use this one. I will explain the trick we have used with our CSS.
First we need to enqueue our CSS. You could also just create a style tag before our form so you do add another HTTP request. I am showing you how to enqueue a style so that you can use that knowledge for your own plugin or theme.
The content of our CSS file is:
The two main containers with the class .file-upload were given a property position with the value relative. By declaring such property we can then position other elements inside that containers how we want. Our input is then positioned absolutely on the container with the widht and height set to 100%. In that way we have stretched our input element across the whole container. We did set the opacity to 0 because in that way the input is still clickable even thought we can’t see it.
With that trick we now have a clickable container which will open the pop-up to select a file for upload.
Here we are setting the path to our file which in this example resides inside the folder js which is inside a folder assets. The file is also dependant of jQuery which we defined inside the array. The last boolean value defines if the script will be placed in the footer or in the header. We are placing it in the footer.
Here we are setting a click event on the element #ibenic_file_upload. This function that we are setting makes sure that the input will be clicked and that the window for choosing a file will open up. We are also setting an event trigger when the value of our input changes. This means that when we add a file to our input element, a function named prepareUpload will trigger. This function will prepare our data with the chosen file.
This function will also create an AJAX call on our site.
Here we are setting some defaults for the AJAX call and the success callback function that will be called when the AJAX is successfully completed. This means that the server returned a 200 response. If there was a 40x or 50x error status an error callback function will be called if that function was defined.
In the success callback function we will need to define everything related to showing the file preview and the delete button, but also hiding the current element that we are using for uploading the file. We will now create the rest of the code for the AJAX success even though we do not have any PHP functionality yet.
Here we are checking if the data object (JSON) has a property named response with a value of SUCCESS. If that is the response value then we will check if the data sent is of type image (jpeg, jpg, png or gif). If that is true then we will create an image element with the url to the image. If the file is not an image then we will just show its filename.
The created image or the filename will be appended to the element #ibenic_file_upload_preview. We target it by getting the ID of the first element which is used to upload the file: ibenic_file_upload. We then concatenate it with the string _preview so that we get the ID of the element for previewing the uploaded file.
We are also setting the uploaded filename on the delete button so that when we click on the delete we will know which file to delete. All that data that we are checking and setting (url, filename, response and type) are actually properties that will be set on the backend code with PHP.
The delete button and the delete functionality will be written later in this tutorial.
Now we will have to create a function that will be used to read the posted data and save the file on the server. Open your plugins’ or themes’ file where you will add that functionality and add this:
Here we are using the action wp_ajax_ibenic_file_upload which is set by our action sent in the AJAX call. This will work only for the logged in users. If you want to enable any visitor of your site to upload their files then you can do that by adding another action:
The only difference is the work nopriv which is added in the action name. Before we begin reading all the POST data we will set a variable with file errors:
Once we have set those errors we can start reading the data. For the first few lines we will read both the global $_POST variable and the $_FILES variable. Then we will merge them together so that we can access all the data under a single variable. Here is the code of that:
At the end of the function we have also added the function die(). Since we are creating a AJAX call on the admin_ajax.php file we do want to stop the reading process when we have already sent the required data as the response. Now with the variable $data we can read all the data that is sent by the AJAX call.
Now that we have set our data in a variable, we can move on and start uploading our file. The first example is an example with the WordPress API wp_handle_upload. To read more about this function follow this link.
We are first defining an empty variable response to hold the response. After that we are using the wp_handle_upload function to upload the file. In this function we are sending the data of the file uploaded. Since we are appending that file data to the key ibenic_file_upload in the AJAX call, we are also referencing that data with the same key. The other data that is passed to this function is an array with some overrides. Since we are not sending this directly from a form but from an AJAX call we will not test the form.
After that we are checking if the file was uploaded and also if there was an error. If there was no error, we are sending a SUCCESS response with the data which we have already defined by setting the AJAX success callback function. If you do not remember them, they were:
If there was an error, then we are just sending the error message with an ERROR response. At the end we are echoing that variable which is encoded in JSON. You can try to upload a file now and see if the image will show in the box or if the filename of another file will be rendered.
Let’s move on another WordPress upload function. This time, the function is the media_handle_upload. We will use the same function and we will not erase the functionality that we have added before. By doing that you can decide how you will upload your file and also with some other posted data we can decide which uploader to use because there can be different situation which will need a different way of uploading the file.
So at the beginning of our function we are defining a variable that will hold the number of the uploader we want to use. Below we have wrapped both uploader in IF statements. So since we are now using the number 2 uploader we are using the media_handle_upload. If you want to learn more about that function visit this link.
I will now explain only the wrapped code under the uploader number 2.
We are immediately calling the mentioned function where we are passing the string which is the key of our file upload. The second parameter is the post ID to which we want to attach this file. Since we have passed a zero (0) this function will not attach the file to any of the posts.
Once we have used that function we are checking if the variable is a WordPress error and if it is, then we are setting the ERROR response and we are getting the error message from our data variable where we have stored information about the uploaded file. If there is an error with the uploaded file, we will have a different number under the key error. We are then passing that number to the array fileErrors which holds all the error description for each error number.
If the attachment ID is not a WordPress error but a real ID we are getting the full path to the uploaded file. Then we are using a PHP function pathinfo to get some data about that file. From that data we can get the filename and the extension. If the extension is of an image type such as a jpg or png, we are extending the variable type with additional info to match the type in the AJAX success function. In that way, if the file is of extension jpg, jpeg, png or gif we will send image/jpg, image/jpeg, image/png or image/gif and not only the extension.
We are also getting the URL of the uploaded file with the WordPress function wp_get_attachment_url.
We will not create our own custom WordPress file upload by uploading our files to the folder custom. This can be defined by you in any way you want. You could also added another layer and upload files to a folder named by the ID of a logged in user or something similar. For this example we will just upload the files to a folder custom.
I will now focus only on the wrapped code for the uploader number 3. Don’t forget to change the number of the variable ibenicUploader to number 3 so that this function can use that uploader.
So at the beginning of that wrapped code, we define several variables where we store information about the path to the upload folder and then using that information we are also creating two other paths which are the paths to our custom folder in the URI and URL form.
We are then checking if that folder exists and if it doesn’t we are creating it. After that we are getting the filename and then we are replacing all the empty places in underscores “_” so a file such as “Picture of me” will be “Picture_of_me”. We are now setting other information about the uploaded file such as the size, error and temporary name.
We are also creating a variable mb which will hold the maximum file size that is allowed to store. After that we are checking if there is an error inside the information about the uploaded file and if there is we are setting the ERROR response with the error message.
If there is no error, we are checking if there is already a file there with the same name and if there is one, we are again setting the ERROR response with the error message that such file already exists.
If there is no file error and the file does not exist already, we are checking for the size of the file. If the file is too big we will set the ERROR response with the error message that the file is too big. But if the file size is under the set size in the variable mb we can start saving the file in our custom folder.
If the file was successfully moved from the temporary folder to our custom folder, we are setting the SUCCESS response with all that required information. If the file was not successfully moved we will again set the ERROR response saying that the file upload failed.
Open our CSS file and at the bottom of it add this few lines:
This will create a function that will append a new div element with the class .alert and append it to our previously defined element .ibenic_upload_message. After the message is shown, it will fade out slowly. To enable this message to show, we will edit our AJAX success callback function:
Here we have added two calls for the function add_message. One is when the response if SUCCESS and one is when the response is ERROR. We have also removed the alert for the error part since now we have a method to show the error message in a fancy way.
When the user click on the Delete button we will read the file url from the data attribute data-fileurl and add that data with the name of the action. Then we will create an AJAX call on it. In the SUCCESS callback function we will hide the preview box and show again the upload box. If the response is ERROR we will just show the error message given from the server.
We now only need to write the code in PHP to handle the deletion of the file, so open the file where we have already written the code for the upload and add this:
Here we are checking if the global $_POST variable is set. If it is set we are getting the information about the file url and then we are checking in the database if there is an attachment with that url. If that return true we are getting the ID of that attachment and then with the WordPress function wp_delete_attachment we are deleting the file.
If we did not get any information from the database then it must be a custom uploaded file. We are then getting the filename from the url and then we are creating the URI to the file. Here we are referencing the path to our custom folder. If you have named your folder differently then create the URI with your name. After that we can use the PHP function unlink and delete the uploaded file.
For the upload and delete to be more secure we should use the WordPress Nonce. First let’s go to our form and add the nonce:
Now lets add that nonce to our AJAX calls. This will only show you where to add that code:
With that nonce added, the AJAX calls will also send the nonce data to our server. We now need to add a simple check for that nonce. Here you can see where to add them:
With this simple steps we have secured our upload and delete with WordPress Nonces.
WordPress file uploads can be done in many different ways. In this article we have covered only a few but there are also some other WordPress API functions that can upload files in WordPress.
Have you ever done something similar or developed a file uploader for WordPress? Share your experience in the comments below.
Web Developer who mainly uses WordPress for projects. Working on various project through Codeable & Toptal. Author of several ebooks at https://leanpub.com/u/igorbenic.