Ajax file upload in WordPress – can’t pass FormData

I’ve made a script which uses $.ajax and FormData to pass two form objects to PHP. One form object is a text and the other is a file.
It worked well as a stand-alone script. However, after I added it to WordPress, as a plugin, it keeps giving me "Uncaught TypeError: Illegal invocation".

I can’t afford to serialize the formdata, simply because then I won’t be able to pass the file to the callback function in PHP.

Read More

JS involving FormData before ajax call:

var fd = new FormData();
var file = jQuery(this).find('input[type="file"]');
var caption = jQuery(this).find('input[name=img_caption]');
var individual_file = file[0].files[0];
fd.append("file", individual_file);
var individual_capt = caption.val();
fd.append("caption", individual_capt);

This part above is 100% correct.

Ajax call:

jQuery.ajax({
    type: 'POST',
    url: fiuajax.ajaxurl,
    data: {
        action: 'fiu_upload_file',
        security: fiuajax.security,
        data: fd,
        contentType: false,
        processData: false,
    },
    success: function(response){
        var dataObj = jQuery.parseJSON(response);
        if(dataObj.message == 'error') {
            jQuery('.fiu_validation').html('The following error occured: '+dataObj.desc);
        }
        else if(dataObj.message == 'success') {
            jQuery('.fiu_file').val('');
        }
        console.log(response);
    }
});

This is incredibly frustrating since it worked perfectly fine outside of WordPress. I’ve tried de-registering WordPress’ jQuery and enqueueing the latest jQuery version, but it made no difference.

To recap:
1) Ajax/jQuery is refusing to pass a form object to PHP
2) Can’t serialize the object because I need to preserve the file object
3) Script works outside of WordPress
4) Tried updating to the newest jQuery version, no change

Related posts

Leave a Reply

5 comments

  1. try this :

    jQuery(document).on('click', '#submit', function(e){
        e.preventDefault();
    
        var fd = new FormData();
        var file = jQuery(document).find('input[type="file"]');
        var caption = jQuery(this).find('input[name=img_caption]');
        var individual_file = file[0].files[0];
        fd.append("file", individual_file);
        var individual_capt = caption.val();
        fd.append("caption", individual_capt);  
        fd.append('action', 'fiu_upload_file');  
    
        jQuery.ajax({
            type: 'POST',
            url: fiuajax.ajaxurl,
            data: fd,
            contentType: false,
            processData: false,
            success: function(response){
    
                console.log(response);
            }
        });
    });
    

    php

    function fiu_upload_file(){
    
        var_dump($_FILES);
        exit();
    }
    
    add_action('wp_ajax_fiu_upload_file', 'fiu_upload_file');
    add_action('wp_ajax_nopriv_fiu_upload_file', 'fiu_upload_file');
    
  2. I managed to do it. The code works on the latest version of WordPress (4.9.4)

    First of all, I’m using XMLHttpRequest to send the data and not the jQuery ajax. This means that you can adapt it to just pure JS.
    Notice the xhttp.setRequestHeader("enctype","multipart/form-data"); which was essential to pass the FormData using this method.

    JS:

    var user_id = $('#user_id').val();
    var picture = $('#userPic')[0].files[0];
    
    console.log(picture);
    
    if( picture && typeof picture !== undefined ) {
    
      if ( picture['size'] > 1000000 ) {
        alert('The Profile Pic can't be larger than 1Mb.');
        return;
      }
    
      if ( picture['type'].indexOf('image') == -1 ) {
        alert('The uploaded file needs to be an image.');
        return;
      }
    
      var data = new FormData();
      data.append('user_id', user_id);
      data.append('userPic', picture);
    
      // console.log(data);
    
      var xhttp = new XMLHttpRequest();
      xhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
          console.log('Receive:' + this.responseText);
        }
      };
      xhttp.open("POST", location.origin + "/wp-content/themes/search-and-go-child/ajax/upload_user_profile_pic_ajax.php", true);
        xhttp.setRequestHeader("enctype","multipart/form-data");
      xhttp.send(data);
    
    }
    

    The PHP part is also extremely useful since it requires the files needed to manipulate the received data with the WP core functions.
    Here you will also find the code to upload an attachment using the WP core functions.

    PHP:

        // error_reporting(-1);
        // ini_set('display_errors', 'On');
        $path = $_SERVER['DOCUMENT_ROOT'];
        require_once($path.'/wp-load.php');
        require_once( '/home/s24t06b21lk5/public_html/wp-includes/template-loader.php' );
        // require_once("/home/s24t06b21lk5/public_html/wp-admin" . '/includes/image.php');
        require_once("/home/s24t06b21lk5/public_html/wp-admin" . '/includes/file.php');
        // require_once("/home/s24t06b21lk5/public_html/wp-admin" . '/includes/media.php');
    
    
        $user_id = $_POST['user_id'];
        $picture = $_FILES['userPic'];
    
    
        var_dump($user_id);
        var_dump($picture);
    
        $response = array();
    
        if( isset($picture['name']) && $picture['name'] ) {
    
            // Get the path to the upload directory.
            $wp_upload_dir = wp_upload_dir();           
    
            $picture['name'] = preg_replace( '/[^0-9a-zA-Z.]/', '', basename( $picture['name'] ) );
    
            // Upload the file
            $upload_overrides = array( 'test_form' => false );
            $upload_result = wp_handle_upload($picture, $upload_overrides);
            // echo '<pre>'; print_r($upload_result); echo '</pre>' ;
    
            if( $upload_result['url'] ) {
    
                // Prepare an array of post data for the attachment.
                $attachment = array(
                    'guid'           => $upload_result['url'], 
                    'post_mime_type' => $picture['type'],
                    'post_title'     => $picture['name'],
                    'post_content'   => '',
                    'post_status'    => 'inherit'
                );
    
    
                $attach_id = wp_insert_attachment( $attachment, $upload_result['file'] );
    
                if( $attach_id ) {
    
                    // Make sure that this file is included, as wp_generate_attachment_metadata() depends on it.
                    require_once( ABSPATH . 'wp-admin/includes/image.php' );
    
                    // Generate the metadata for the attachment, and update the database record.
                    $attach_data = wp_generate_attachment_metadata( $attach_id, $upload_result['file'] );
                    wp_update_attachment_metadata( $attach_id, $attach_data );
    
                    // Update the usermeta table with the uploaded avatar
                    if( !update_user_meta($user_id, 'wp_user_avatar', $attach_id ) || !update_user_meta($user_id, 'wp_zfgrf5v7rw_user_avatar', $attach_id) ) {
                        $response['result'] = FALSE;
                        $response['message'] = "The uploaded image could not be associated with the User ID in the database.";              
                    }
                    else {
                        $response['result'] = TRUE;             
                    }
    
    
                }
                else {
                    $response['result'] = FALSE;
                    $response['message'] = "Wordpress attachment post for the user's image could not created.";
                }
    
            }
            else {
                $response['result'] = FALSE;
                $response['message'] = "The file couldn't be uploaded. Check the permissions.";
            }
    
        }
    
        die( json_encode($response) );
    

    Thank you.

  3. Works whith any input(one or many simple or multiple), textarea, select in your form (WP 5.0.3)

        $('#form').submit(function(e) {
            e.preventDefault();
    
            var form = $(this);
            var formdata = (window.FormData) ? new FormData(form[0]) : null;
            var data = (formdata !== null) ? formdata : form.serialize();
    
            formdata.append("action", "fiu_upload_file");
    
            $.ajax({
                type: 'POST',
                url: fiuajax.ajaxurl,
                contentType: false,
                processData: false,
                dataType: 'JSON',
                status: 200,
                data: formdata,
                success: function(data){                    
                    if(data.error == 'true') {
                        $('.msg').html(data.true);                          
                    }
                    else {
                        $('.msg').html(data.false); 
                        // your code if you want an action ...                                                                          
                    };
                }
            });
        });
    

    and php for only the files

        foreach ($_FILES as $file) :
            if($file['error'] == UPLOAD_ERR_NO_FILE) :
                continue;
            endif;
    
            $valid_ext = array( 'jpg' , 'jpeg' , 'png' , 'doc' , 'docx' , 'pdf' , 'xls' , 'xlsx');
            $extension_upload = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
            if ( in_array($extension_upload,$valid_ext) ) :
                $name_upload = uniqid() . $file['name'];
                $url_insert = trailingslashit( plugin_dir_path( dirname( __FILE__ ) ) ) . 'uploads';
                wp_mkdir_p($url_insert);
                $name_insert = trailingslashit($url_insert) . $name_upload;
                $action = move_uploaded_file($file['tmp_name'],$name_insert);
                $msg_true = 'Upload ok ';
            else :
                $msg_error = 'Upload error';
            endif;
        endforeach;
    
        $result = !isset($msg_error);
        $msg = array();
    
        if($result) :
            $msg['error'] = 'true';
            $msg['true'] = $msg_true;
        else :
            $msg['error'] = 'false';
            $msg['false'] = $msg_error;
        endif;
    
    
    
        header('Content-Type: application/json');
        echo json_encode($msg);
    
        
    
  4. I added

     dataType: 'json'
    

    and it helps.
    Full listing of your code of Ajax call:

    jQuery.ajax({
        type: 'POST',
        url: fiuajax.ajaxurl,
        data: {
            action: 'fiu_upload_file',
            security: fiuajax.security,
            data: fd,
            dataType: 'json',
            contentType: false,
            processData: false,
        },
        success: function(response){
            var dataObj = jQuery.parseJSON(response);
            if(dataObj.message == 'error') {
                jQuery('.fiu_validation').html('The following error occured: '+dataObj.desc);
            }
            else if(dataObj.message == 'success') {
                jQuery('.fiu_file').val('');
            }
            console.log(response);
        }
    });
    
  5. here some modified code in case you have multiple data and multiple files

    var fd = new FormData();
    var data = jQuery('#yourformID').serializeArray();
    jQuery.each(data,function(key,input){
        fd.append(input.name,input.value);
    });
    var file = jQuery(document).find('input[type="file"]');
    jQuery.each(jQuery(file), function(i, obj) {
        jQuery.each(obj.files,function(j,file){
            fd.append('files[' + j + ']', file);
        })
    });
    fd.append('action', 'fiu_upload_file');