0

I'm new to JS and I've to do a feature at work that I don't know very well how deal with it.

I have a form, where the user can add items dynamically pressing buttons. One of the buttons allows to add an input to upload a file. For symplicity I will resume the HTML to something like this:

The original div in the form it's:

<div id="userContent">

</div>

When the user press the button it adds elements, in this case file inputs:

<div id="userContent">
    <div id="inputFile-1" class="customTabContent"> 
        <input id="file-1" type="file" required="required">
    </div>
    <div id="inputFile-2" class="customTabContent"> 
        <input id="file-2" type="file" required="required">
    </div>
    <div id="inputFile-3" class="customTabContent"> 
        <input id="file-3" type="file" required="required">
    </div>
</div>

When the user finish adding elements and press the button to submit the data, I read the DOM and to see what has created the user, first I get the parent with:

var parent = document.getElementById('userContent');

Then, I iterate over the childs and for each child I get the input:

var input = document.getElementById('file-1');
var file = input.files[0];

And then I use this function to read the file and return the value in base64, using closures to obtain the value:

function getBase64(file) {
    var base64data = null;
    var fileReader = new FileReader();
    fileReader.onloadend = (function () {
        return function () {
            var rawData = fileReader.result;
            /* Remove metadata */
            var cleanedData = rawData.replace(/^data:(.*;base64,)?/, '');
            /* Ensure padding if the input length is not divisible by 3 */
            if ((cleanedData.length % 4) > 0) {
                cleanedData += '='.repeat(4 - (cleanedData.length % 4));
            }
            base64data = cleanedData;
        }
    })();
    fileReader.readAsDataURL(file);

    return base64data;
}

My problem it's that on the website, I get an error saying that base64data it's null but if I put a breakpoint on return base64data; at the end of getBase64(file) the variable has the value on base64, and if I release the debugger I can send the value to the server.

Reading the documentation, FileReader it's asynchronous so I think that the problem seems be related to it, but how can I achieve what I want to do? I don't know why on the debugger I can get the value but outside the debugger no. I'm very stuck....

Regards.

P.D: If I don't answer it's because I'm not at work, so sorry for the delay on the response.

-------- EDIT 1

Thanks to Sjoerd de Wit I can get the result but I can't assign to a variable:

var info = {'type': '', 'data': ''};

........
} else if (it_is_a_file) {
    var file = input.files[0];
    var base64data = null;
    getBase64(file).then((result) => {
        base64data = result;
        info['type'] = 'file';
        info['data'] = base64data;
    });
} else if 
.......
return info;

But on info I get {'type': '', 'data': ''}. I'm using wrong the promise? Thanks.

-------- EDIT 2

This problem was because as a noob on JavaScript, I didn't know that using FLASK, you have to use forms and get the data in a different way.

So, the answer to this question was search how get data from a FORM with FLASK.

But I'm going to mark the answer as correct beacuse you can get the value, as I was asquing for.

MinionAttack
  • 513
  • 1
  • 11
  • 26
  • Promise is an async callback. So your application flow goes on and your return value is not set at this point. – Dani R Apr 11 '19 at 15:12
  • ps: you probably don't even need this FileReader, generating a dataURL from front-end is most of the time not what you want. If you wish to consume this dataURL from front, use a [BlobURL](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL) instead; if you wish to send the data to your server, send it as binary. – Kaiido Apr 11 '19 at 15:15
  • @Kaiido I don't understand, all tutorials I've seen on internet uses 'readAsDataURL' to read a file that you upload via an input file. I want to load it syncronous because it will be a small file so won't be lag on the browser. – MinionAttack Apr 12 '19 at 07:16
  • Well... All the tutorials you saw are wrong (or were written in the 6 months period where `FileReader.readAsDataURL` was available but not `URL.createObjectURL`). If you wish for instance to display an Image from an user povided file, all it takes is `input.onchange = e => img.src = URL.createObjectURL(input.files[0])`. The only cases where you may need a dataURL is to generate standalone documents, that should embed binary data. That's quite rare. – Kaiido Apr 12 '19 at 07:21

1 Answers1

1

You could turn the function to return a promise and then resolve the base64data when it's loaded.

    function getBase64(file) {
    return new Promise((resolve, reject) => {
      var fileReader = new FileReader();

      fileReader.onloadend = (function () {
          return function () {
              var rawData = fileReader.result;
              /* Remove metadata */
              var cleanedData = rawData.replace(/^data:(.*;base64,)?/, '');
              /* Ensure padding if the input length is not divisible by 3 */
              if ((cleanedData.length % 4) > 0) {
                  cleanedData += '='.repeat(4 - (cleanedData.length % 4));
              }
              resolve(cleanedData);
          }
      })();

      fileReader.readAsDataURL(file);
    });
  }

Then where you want to read it you can do:

  getBase64(file).then((base64data) => {
    console.log(base64data);
  })
Sjoerd de Wit
  • 2,353
  • 5
  • 26
  • 45
  • Hi! Thanks for the response, I get the result but I'm unable to assign it to a variable. I've updated the question with the details. Thanks for your help. – MinionAttack Apr 11 '19 at 15:00