3

What I am trying to do:

Essentially I am trying to upload a file to a directory on the server that a user uploads via a HTML <input type="file"> element.

To do this I am creating a new XMLHttpRequest on the <input> element's change event which should send the data of the uploaded file to the upload.php file which would then handle the uploaded file and upload it to the server asynchronously.

My Code:

HTML

<form class="js-upload-form" method="POST" action="upload.php" enctype="multipart/form-data">
    <input class="button js-uploaded-file" type="file" name="file" />
</form>

JS

document.querySelector('.js-uploaded-file').addEventListener('change', function() {

    let file = this.files[0];
    let formData = new FormData();

    formData.append('file', file);

    let xhr = new XMLHttpRequest();
    xhr.open('POST', 'upload.php', true);

    xhr.setRequestHeader('Content-type', 'multipart/form-data');

    xhr.upload.onprogress = function(e) {

        if (e.lengthComputable) {

            let percentComplete = (e.loaded / e.total) * 100;
            console.log(percentComplete + '% uploaded');

        }
    };

    xhr.onload = function() {

        if (this.status == 200) {

            console.info(this.response);

        }
    };

    xhr.send(formData);

}, false);

PHP (upload.php)

print_r($_FILES);

$currentDir = getcwd();
$uploadDirectory = "/uploads/";

$errors = []; // Store all foreseen and unforseen errors here

$fileName = preg_replace("/[^A-Z0-9._-]/i", "_", $_FILES['file']['name']);
$fileSize = $_FILES['file']['size'];
$fileTmpName = $_FILES['file']['tmp_name'];
$fileType = $_FILES['file']['type'];

$uploadPath = $currentDir . $uploadDirectory . $fileName;

if ($fileSize > 2000000) {
    $errors[] = "This file is more than 2MB. Sorry, it has to be less than or equal to 2MB";
}

if (empty($errors)) {
    $didUpload = move_uploaded_file($fileTmpName, $uploadPath);

    if ($didUpload) {
        echo "The file " . basename($fileName) . " has been uploaded";
    } else {
        echo "An error occurred somewhere. Try again or contact the admin";
    }
} else {
    foreach ($errors as $error) {
        echo $error . "These are the errors" . "\n";
    }
}

My Problem

This code is simply not working. Printing the $_FILES array returns an empty array, and console logging the xhr.response logs the error message set in the PHP ('An error occurred somewhere. Try again or contact the admin'). I would really appreciate any help on solving this issue as I have looked through countless other online resources regarding this issue and even though I feel like my code does exactly what they all say to do, it still doesn't work.

What I've tried:

I tried simply submitting the form instead of trying to do so using the FormData() object and <input> change event by adding a submit button and although the page redirected to ...url/upload.php and didn't work asynchronously, the $_FILES array contained the correct data of the uploaded file and the file was uploaded to the server, which makes me think there must be an issue in my Javascript code, either relating to the XMLHttpRequest or the FormData object.

6
  • Is this issue in IE or in any other browser? Commented Feb 28, 2019 at 12:17
  • Can you try this without the setRequestHeader line? The example under developer.mozilla.org/en-US/docs/Web/API/FormData/… does not bother manually specifying the Content-Type either. Commented Feb 28, 2019 at 12:19
  • @AndreiLupuleasa I'm working on Chrome but I assume every browser Commented Feb 28, 2019 at 12:27
  • @04FS wow so that fixed it... every example I've come across explicitly states that line is necessary but removing it makes it work... strange but thank you so much! Feel free to write that up as an answer and I'll accept it, if not I can write the answer up for you :) Commented Feb 28, 2019 at 12:29
  • Than you should look in dev tools to see if your file is actually sent. Commented Feb 28, 2019 at 12:29

1 Answer 1

5

Normally, XMLHttpRequest will generate an appropriate Content-Type from the FormData object.

You, however, are overriding it with one you created manually:

xhr.setRequestHeader('Content-type', 'multipart/form-data');

However, the mandatory boundary parameter is missing, so PHP can't find the points to split the parts of the request up.

Don't override the Content-Type. Remove the quoted line.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.