Single file upload using WordPress Ajax

I recently had to amend an existing form on a website that needed a file input adding to it. The form was already submitting via a wordpress ajax and I found documentation on how to go about it a little sparse and contradictory.

Here is a simplified setup to get you uploading and saving files to the media library using a frontend ajax form…

First we need a form with a file input:

<form action="" enctype="multipart/form-data" id="my_form">
    <!-- add a file input -->
    <input type="file" name="my_file" />
    <!-- set the ajax action to be completed later in PHP -->
    <input type="hidden" name="action" value="file_upload_ajax_handler" />
</form>

We now need to set the ajax url for use in a jquery/ajax function. This goes in your functions.php:

<?php
add_action('wp_head', 'add_ajaxurl');
function add_ajaxurl() {
    echo '<script type="text/javascript">var ajaxurl = "'.admin_url('admin-ajax.php') . '";</script>';
}

Next we need a jQuery function to handle the submission of the form:

$('#my_form').on('submit', function(e){
    e.preventDefault(); // stop the form submitting
    var form_data = new FormData(this);
    $.ajax({
        url: ajaxurl,
        type: 'POST',
        contentType: false,
        cache: false,
        processData:false,
        data: form_data,
        success: function (ajax_response) {
            // file was uploaded success fully do something here...
            var response = JSON.parse(ajax_response);
            console.log(response);
        },
        error: function (errorThrown) {
            console.log(errorThrown);
        }
    });
    
});

The jQuery above will send the form data to the WordPress ajax handler and in your functions.php file you need to have a function of the same name we specified in hidden field of the form (file_upload_ajax_handler):

function file_upload_ajax_handler() {
    // set up a response array
    $response = [];

    // you could handle and process other form fields here first like inserting a new post using $_RESPONSE parameters eg. $_RESPONSE['first_name']

    // for WordPress to handle the upload and it be accessible in the media library you need to include these files 
    require_once( ABSPATH . 'wp-admin/includes/image.php' );
    require_once( ABSPATH . 'wp-admin/includes/file.php' );
    require_once( ABSPATH . 'wp-admin/includes/media.php' );
    
    // handle the upload using the name attribute of the file input 'my_file'.
    // the second value 0 can also be a post id to associate the attachment to but by using 0 you are declaring it has no post.
    $attachment_id = media_handle_upload('my_file', 0);

    // check if we had any errors
    if ( is_wp_error( $attachment_id ) ) {
        $response['uploaded'] = false;
        $response['attachment_id'] = null;
        $response['message'] = 'There was an problem uploading your file';
    } else {
        $response['uploaded'] = true;
        $response['attachment_id'] = $attachment_id;
        $response['message'] = 'File Uploaded successfully';
    }
    
    // return the $response array so you can handle any frontend sucess or failure messages...
    return json_encode($response);
    // always die or exit an ajax function
    wp_die();
}

add_action('wp_ajax_file_upload_ajax_handler', 'file_upload_ajax_handler');
add_action('wp_ajax_nopriv_file_upload_ajax_handler', 'file_upload_ajax_handler');    

Just to reiterate, this is a simplified way of getting a file upload working with WordPress ajax but if you know a better or different way to do it then please do leave a comment…