php - Upload multiple files via ajax from an HTML file input

admin2025-01-07  3

I'm trying to upload files from a multiple file upload HTML input via ajax on a WordPress site but I keep getting a 400 error and I'm not sure why. My code is based on this tutorial.

jQuery:

$(document).on('change','input[type="file"]',function(){

  var fd = new FormData();
  var files_data = $(this); // The <input type="file" /> field

  // Loop through each data and create an array file[] containing our files data.
  $.each($(files_data), function(i, obj) {
      $.each(obj.files,function(j,file){
          fd.append('files[' + j + ']', file);
      })
  });

  fd.append('action', 'file_upload');

  fd.append('nonce', $('#file_upload_nonce').val());

  fd.append('application_id', $(this).closest('.application_question').attr('data-application-id'));

  $.ajax({
      type: 'POST',
      url: '/wp-admin/admin-ajax.php',
      data: fd,
      contentType: false,
      processData: false,
      success: function(response){
        console.log(response);
      }
  });

});

PHP:

function file_upload(){
  // Check the nonce first
  if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'file_upload' ) ) {
    echo 'Security validation failed.';
  } else {

    $application_id = $_POST['application_id'];

    foreach ( $_FILES['files']['name'] as $f => $name ) {

      move_uploaded_file( $_FILES["files"]["tmp_name"][$f], '/wp-content/supporting-evidence/' . $application_id . '/' . $_FILES["files"]["name"][$f] );

    }

  }
  wp_die();
}
add_action('wp_ajax_file_upload','file_upload');

What am I doing wrong here?

I'm trying to upload files from a multiple file upload HTML input via ajax on a WordPress site but I keep getting a 400 error and I'm not sure why. My code is based on this tutorial.

jQuery:

$(document).on('change','input[type="file"]',function(){

  var fd = new FormData();
  var files_data = $(this); // The <input type="file" /> field

  // Loop through each data and create an array file[] containing our files data.
  $.each($(files_data), function(i, obj) {
      $.each(obj.files,function(j,file){
          fd.append('files[' + j + ']', file);
      })
  });

  fd.append('action', 'file_upload');

  fd.append('nonce', $('#file_upload_nonce').val());

  fd.append('application_id', $(this).closest('.application_question').attr('data-application-id'));

  $.ajax({
      type: 'POST',
      url: '/wp-admin/admin-ajax.php',
      data: fd,
      contentType: false,
      processData: false,
      success: function(response){
        console.log(response);
      }
  });

});

PHP:

function file_upload(){
  // Check the nonce first
  if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'file_upload' ) ) {
    echo 'Security validation failed.';
  } else {

    $application_id = $_POST['application_id'];

    foreach ( $_FILES['files']['name'] as $f => $name ) {

      move_uploaded_file( $_FILES["files"]["tmp_name"][$f], '/wp-content/supporting-evidence/' . $application_id . '/' . $_FILES["files"]["name"][$f] );

    }

  }
  wp_die();
}
add_action('wp_ajax_file_upload','file_upload');

What am I doing wrong here?

Share Improve this question asked Feb 7, 2023 at 11:37 AJTAJT 1871 silver badge11 bronze badges 5
  • Where did you put your PHP code, is it in the theme functions file? Were you logged-in when you tried your form? – Sally CJ Commented Feb 7, 2023 at 13:15
  • The PHP is within a plugin file and I was logged in when I tested the form (it's designed to only be accessed by logged in users). EDIT: You've just helped me realise that I wasn't @includeing the PHP file (doh!) I'm not getting the 400 error now, but the files aren't being uploaded. – AJT Commented Feb 7, 2023 at 13:28
  • Have you checked, via the browser console, whether the AJAX request is being sent properly? If yes, try adding a echo 'it works'; after the foreach loop in your PHP function, then see if the AJAX response includes that it works text. – Sally CJ Commented Feb 7, 2023 at 14:22
  • I can see in the comments you are no longer getting the 400 error - did you find a solution? Can you check if you var_dump($_FILES) in your php file if there are any files being sent over ? Have you included enctype="multipart/form-data" as an attribute on the form which you are submitting? I can see you are using the application id to organise your folders within supporting evidence - do you need to create the directory before uploading the file to that location using mkdir() function i.e. if(!is_dir('/wp-content/supporting-evidence/'.$application_id)) { mkdir('/wp-content/supporting-ev – Jaimee Page Commented Feb 7, 2023 at 16:49
  • @AJT can you leave that you didn't include the file as an answer? It's not obvious that you found the solution, the site doesn't know this and people don't always leave comments – Tom J Nowell Commented Feb 8, 2023 at 14:00
Add a comment  | 

1 Answer 1

Reset to default 0

It's because you're logged out and the code does not handle that

There is also a major security hole in the code, scroll down to see the details

HTTP 400 and a 0 is what admin-ajax.php returns when it cannot find a handler for your AJAX request.

Note that if you had used the modern REST API instead of the legacy admin-ajax.php API it would have told you this in plaintext human readable language and a 404 like this:

{"code":"rest_no_route","message":"No route was found matching the URL and request method.","data":{"status":404}}

It would also have responded with a 403 forbidden if you'd made the same mistake, which is much harder to make when using AJAX with a REST API endpoint

If we look at the tutorial we can see where it adds the AJAX handler action:

add_action('wp_ajax_cvf_upload_files', 'cvf_upload_files');
add_action('wp_ajax_nopriv_cvf_upload_files', 'cvf_upload_files'); // Allow front-end submission

But, if we look at the code in your question:

add_action('wp_ajax_file_upload','file_upload');

Your code has 1 action, the tutorial has 2. You're missing the wp_ajax_nopriv_ version.

Overrall, keep in mind this is an 8 year old tutorial from 2015, and you shouldn't follow just a single tutorial when researching things. The official WordPress developer docs explain this much better, and since that tutorial was written a new modern API for AJAX called the REST API was written.

Further Notes

  • you never checked that move_uploaded_file worked or not, it returns a false value if it failed, you need to check for this or it will fail silently and you'll be scratching your head trying to figure out why files are missing
  • it needs to create the folder!
  • you should not assume the location of the upload folder, the tutorial you shared uses wp_upload_dir() instead of assuming /wp-content/uploads to do this
  • What if $_POST['application_id'] contained something malicious? e.g. ../? or myapplicationID/and/this/is/a/subfolder?
  • the AJAX happens every time the file input changes, which means if I accidentally pick the wrong file, it's instantly uploaded and I can't get it back! What if I picked something confidential by accident that was next to it and now we're both in trouble? Or if I select the same file 5 times, now there are 5 copies on your server. What if I accidentally select a 24GB blueray backup file?
    • the tutorial also has a check for this, but your code does not

转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1736261311a732.html

最新回复(0)