XHR POST request with multi files using FileReader API

322 Views Asked by At

This is my first question on StackOverflow :)

I'm learning Javascript for a personal project and I get some troubles with asynchronous functions. Something is not clear to me yet with such functions :( .

I try to upload multifiles in an HTML form to prepare an XHR request. Below is my function that I trigger with a an AddEventListener on the submit button.

I found explications on MDN learning web development but since it was only for one file I custom it with a for loop on formCell[5].files (declared as global constant) which is where my files are.

The problem seems to be with the asynchronous behavior. Is an expert on stack have advice to me ? Is there a solution with promises for example ? The "If" with SetTimeOut to wait for loop execution don't seem to me very pretty.

I think I tried hundred solutions without success :)

Thanks a lot in advance,

Regards, Romain

function sendData() {
    /* Loading files */
    var binaryFiles = [null];

    for (let i = 0; i < formCell[5].files.length; i++) {
        let reader = new FileReader(); // FileReader API
        reader.addEventListener("load", function () { // Asynchronous function (return result when read)
            binaryFiles[i] = reader.result;
        });
        // Read the file
        reader.readAsBinaryString(formCell[5].files[i]);
    }

    if(binaryFiles.length !== formCell[5].files.length) {
        setTimeout( sendData, 10 );
        return;
    }
    
    console.log("final" + binaryFiles.length);

    const XHR = new XMLHttpRequest();
    const boundary = "blob"; // We need a separator to define each part of the request
    let msg = "";

    /* Loading files in the request */
    for (let i = 0; i < formCell[5].files.length; i++) {
        msg += "--" + boundary + "\r\n";
        msg += 'content-disposition: form-data; '
            + 'name="'         + formCell[5].name          + '"; '
            + 'filename="'     + formCell[5].files[i].name + '"\r\n';
        msg += 'Content-Type: ' + formCell[5].files[i].type + '\r\n';
        msg += '\r\n';
        msg += binaryFiles[i] + '\r\n';
    }

    /* Loading texts in the request */
    for (let i = 0; i < formCell.length - 1; i++) {
        msg += "--" + boundary + "\r\n";
        msg += 'content-disposition: form-data; name="' + formCell[i].name + '"\r\n';
        msg += '\r\n';
        msg += formCell[i].value + "\r\n";
    }

    msg += "--" + boundary + "--";

    XHR.addEventListener("load", function(event) {
        alert( 'Yeah! Data sent and response loaded.' );
    });
    XHR.addEventListener("error", function(event) {
        alert("Oops! Something went wrong.");
    } );
    XHR.open("POST", "http://localhost:3000/upload");
    XHR.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
    XHR.send(msg);
    console.log(msg);
}

1

There are 1 best solutions below

0
Romain On

I think I finished by solving my problem using Promises :)

If anyone could confirm me that the code below is correct it could help me :)

Thanks, Romain

function fileUpLoad(fileToUpload) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader(); // FileReader API
        
        reader.addEventListener("load", function () { // Asynchronous function (return result when read)
            resolve(reader.result);
            reject(reader.error);
        });
        // Read the file
        reader.readAsBinaryString(fileToUpload);
    });
}

/* Sending message */
function sendData(filesUploaded) {
    let binaryFiles = filesUploaded;

    const XHR = new XMLHttpRequest();
    const boundary = "blob"; // We need a separator to define each part of the request
    let msg = "";

    /* Loading files in the request */
    for (let i = 0; i < binaryFiles.length; i++) {
        msg += "--" + boundary + "\r\n";
        msg += 'content-disposition: form-data; '
            + 'name="'         + formCell[5].name          + '"; '
            + 'filename="'     + formCell[5].files[i].name + '"\r\n';
        msg += 'Content-Type: ' + formCell[5].files[i].type + '\r\n';
        msg += '\r\n';
        msg += binaryFiles[i] + '\r\n';
    }

    /* Loading texts in the request */
    for (let i = 0; i < formCell.length - 1; i++) {
        msg += "--" + boundary + "\r\n";
        msg += 'content-disposition: form-data; name="' + formCell[i].name + '"\r\n';
        msg += '\r\n';
        msg += formCell[i].value + "\r\n";
    }

    msg += "--" + boundary + "--";

    XHR.addEventListener("load", function(event) {
        alert( 'Yeah! Data sent and response loaded.' );
    });
    XHR.addEventListener("error", function(event) {
        alert("Oops! Something went wrong.");
    } );
    XHR.open("POST", "http://localhost:3000/upload");
    XHR.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
    XHR.send(msg);
    console.log(msg);
}

        /* Validation on submit calling */
        form.addEventListener("submit", function (evt) {
            evt.preventDefault();
            if (validationOnSubmit()) {
                if (formCell[5].files.length > 0) {
                    let fileUploadingPromise = []
                    for (let i = 0; i < formCell[5].files.length; i++) {
                        fileUploadingPromise[i] = fileUpLoad(formCell[5].files[i]);
                    }
                    let binaryFiles = [null];
                    Promise.all(fileUploadingPromise)
                        .then (resolve => {
                            for (let i = 0; i < formCell[5].files.length; i++) {
                                binaryFiles[i] = resolve[i];
                            }
                            sendData(binaryFiles)
                        })
                        .catch (reject => {
                            console.log(reject);
                        });
                } else {
                    sendData(0);
                }
            }
        });