0

I have the following JS code that I use to upload an image based on its path:

var data = new FormData();
data.append('fileName', fileName);
data.append('file', file);
$.ajax({
    url : dbPath + "upload-file.php",
    type: 'POST',
    data: data,
    contentType: false,
    processData: false,
    mimeType: "multipart/form-data",
    success: function(data) {
       Swal.close();

       var fileURL = dbPath + data;
       console.log('FILE UPLOADED TO: ' + fileURL);

This is my upload-image.php script:

<?php

$fileName = $_POST['fileName'];

if ($_FILES["file"]["error"] > 0) {
    echo "Error: " .$_FILES["file"]["error"]. "<br>";

} else {
    // Check file size
    if ($_FILES["file"]["size"] > 20485760) { // 20 MB
        echo "Sorry, your file is larger than 20 MB. Upload a smaller one.";    
    } else { uploadImage(); }

}// ./ If


// UPLOAD IMAGE ------------------------------------------
function uploadImage() {
    // generate a unique random string
    $randomStr = generateRandomString();
    $filePath = "uploads/".$randomStr."".$fileName;

    // upload image into the 'uploads' folder
    move_uploaded_file($_FILES['file']['tmp_name'], $filePath);

    // echo the link of the uploaded image
    echo $filePath;
}

// GENERATE A RANDOM STRING ---------------------------------------
function generateRandomString() {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $randomString = '';
    for ($i = 0; $i<20; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;
}
?>

Everything works fine in a matter of uploading a file, a JPG image for example, but my purpose is to use:

data.append('fileName', fileName);

to simply append the file name and extension to my FormData and get a file URL like this:

https://example.com/_json/uploads/03aC8qsIk4hanngqO3G4_fileName.jpg

But the $fileName variable in my PHP script fires an error_log line as Undefined index fileName in line xx, so is there a way to upload a file, get its name and extension and append it to the file URL that my PHP script generates?

I hope my question is clear, what I'm trying to do is simply a general Ajax automatic upload for any kind of file, not just images, and get their URL based on a random string + its name and extension (like .png, .mp4, .pdf, etc.).

Frank Eno
  • 2,581
  • 2
  • 31
  • 54
  • 1
    FormData has a gotcha - you have to append your file last. So do `data.append('file', file);` *after* you append the filename. – arbuthnott Jan 09 '20 at 16:28
  • 1
    Using a filename given by a client is a bad idea. You've to check the name carefully for the path injection. – Teemu Jan 09 '20 at 16:29
  • What comes to the question itself, it's a bit ambiguous, because in PHP there's no `undefined`. If that's the value of `$_POST['fileName']`, that means that the JS variable `fileName` is `undefined` at the time you append it to the form. – Teemu Jan 09 '20 at 16:37
  • @Teemu i get this in the error_log: Undefined index: fileName – Frank Eno Jan 09 '20 at 16:46
  • Well, you should put that on the question, the error message makes it clear what is going on. Have you checked the value of `fileName` at JS before appending? If it's an empty string, the item is not posted. I have never experienced the gotcha arbuthnott introduced, I've appened a file to arbitrary positions in the form, and always have had all the items posted. – Teemu Jan 09 '20 at 16:49
  • Yes, in my ajax function I can successfully get the file name, I just don't know how to pass it to my php script, I get this error_log: **Undefined index: fileName** – Frank Eno Jan 10 '20 at 06:40
  • @arbuthnott I've tried to do what you said, but no success, same issue, the fileName is Undefined in the php script, valid in my ajax function – Frank Eno Jan 10 '20 at 06:42
  • Change the `contentType` argument from `false` to `multipart/form-data; charset=UTF-8`, which is needed when a file is attached. With the current code, your server might get confused because of the lacking Content-Type header. – Teemu Jan 10 '20 at 07:15
  • Thanks, I did, but it doesn't upload any file them so I've tried adding **mimeType: "multipart/form-data"**, but no scuccess, I just cannot append my file name to fileRL string :( – Frank Eno Jan 10 '20 at 07:39
  • As a last resort, try to post the request with native JS (see an example [at the end of this my answer](https://stackoverflow.com/a/53525555/1169519)). If that won't work for you, it has to be something in your server configuration ... – Teemu Jan 10 '20 at 08:09
  • I've tried using your last code, but I get this: 'Uncaught TypeError: data.includes is not a function at XMLHttpRequest. (add.php:103)' – Frank Eno Jan 10 '20 at 09:59
  • There's no such a line in my code ..? About the server configs. I've done some searching, it's possible, that your image is bigger, than the maximum allowed filesize to upload, or especially, the size of the image and the text together exceed the limit. The applicable directives are `upload_max_filesize` and `post_max_size`, which are placed in php.ini file. Specifically exceeding the latter limit could be a reason, why `fileName` is missing. – Teemu Jan 10 '20 at 10:13

1 Answers1

0

I've figured it out, thanks to all the comments, I had to append the fileName to my FormData() and edit my PHP script as it follows:

JS:

function uploadFile() {
var dbPath = '<?php echo $DATABASE_PATH ?>';
    var file = $("#file")[0].files[0];
    var fileName = file.name;
    console.log('FILE NAME: ' + fileName);

    Swal.fire({icon: 'success', title: 'Loading...', showConfirmButton: false })

    var data = new FormData();
    data.append('file', file);
    data.append('fileName', fileName); <!-- ADDED THIS LINE

    $.ajax({
        url : dbPath + "upload-file.php?fileName=" + fileName, <!-- ADDED THIS LINE
        type: 'POST',
        data: data,
        contentType: false,
        processData: false,
        mimeType: "multipart/form-data", <!-- ADDED THIS LINE
        success: function(data) {
            Swal.close();

            var fileURL = dbPath + data;
            console.log('FILE UPLOADED TO: ' + fileURL);
            // error
            if (data.includes("ERROR:")) {
                Swal.fire({ icon: 'error', title: 'Oops...', text: data, });
            // show file data
            } else {
                $("#fileURL").val(fileURL);
                $("#viewButton").attr("href", fileURL);
                $("#viewButton").css("display", "block");
            }
        // error
        }, error: function(e) {  
            Swal.fire({ icon: 'error', title: 'Oops...', text: 'Something went wrong: ' + e.message, });
    }});
}

My upload-file.php script:

<?php include 'config.php';

if ($_FILES["file"]["error"] > 0) {
    echo "Error: " .$_FILES["file"]["error"]. "<br>";

} else {
    // Check file size
    if ($_FILES["file"]["size"] > 20485760) { // 20 MB
        echo "ERROR: Your file is larger than 20 MB. Please upload a smaller one.";    
    } else { uploadImage(); }

}// ./ If


// UPLOAD IMAGE ------------------------------------------
function uploadImage() {
    // generate a unique random string
    $randomStr = generateRandomString();
    $filePath = "uploads/".$randomStr;

    // upload image into the 'uploads' folder
    move_uploaded_file($_FILES['file']['tmp_name'], $filePath);

    // echo the link of the uploaded image
    echo $filePath;
}

// GENERATE A RANDOM STRING ---------------------------------------
function generateRandomString() {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $randomString = '';
    for ($i = 0; $i<20; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString."_".$_POST['fileName']; <!-- ADDED THIS LINE
}
?>

Now I can finally upload any type of file and get its name + extension at the end of the URL, it works perfectly.

Frank Eno
  • 2,581
  • 2
  • 31
  • 54
  • WUT! You have missed the line `data.append('fileName', fileName);` in your real code, but have included it into the example in the post ..? – Teemu Jan 10 '20 at 10:20
  • No, but that was the first suggestion from @arbuthnott 's comment – Frank Eno Jan 10 '20 at 10:21