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?
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.
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 missingwp_upload_dir()
instead of assuming /wp-content/uploads
to do this$_POST['application_id']
contained something malicious? e.g. ../
? or myapplicationID/and/this/is/a/subfolder
?
@include
ing 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:28echo 'it works';
after theforeach
loop in your PHP function, then see if the AJAX response includes thatit works
text. – Sally CJ Commented Feb 7, 2023 at 14:22var_dump($_FILES)
in your php file if there are any files being sent over ? Have you includedenctype="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 usingmkdir()
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:49include
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