Search code examples
google-apps-scriptweb-applicationsfilereadermultifile-uploadermulti-upload

Data from objects created from FileReader in a web app then passed to Google Apps Script client-side return empty, undefined, null


My web app has a few file input elements where the user can upload any number of files of different types. I'm trying to store all data first in an array the same size of the number of input elements, where each item in the said array contains an array of data (filename, blob, mimetype) from each uploaded file.

The user can choose to upload or not, but if they don't, I still need to save some reference for that row with an empty upload by getting its data attribute (header).

The data appears in the browser console, but it returns undefined/empty/null when accessed from the client side of Google Apps Script. I'm not very familiar with the FileReader.

On the server side, I want to create a file in Google Drive from each blob and finally store the URL in Sheets.

Thanks in advance!

function saveFormData(){

  var fileInputs = document.querySelectorAll('tr.result-row td > input.upload');
  var firstUploadIndex = fileInputs[0].parentElement.parentElement.getAttribute('data-index');

  //Loop through all upload rows
  var uploads = []; //will be array of arrays of multi-uploaded files; each item inside belongs to 1 column in db
  Array.from(fileInputs).forEach(input => {

    var files = input.files; //object file list

    if(files.length < 1){ //if there's no file in the upload input
      var header = input.getAttribute('data-uploadtype'); //save the header name as reference
      var fileData = [header, '', '', '']; 
      uploads.push(fileData);
    } else { //if there's a file/s in the upload input
      var innerUploads = []; //array of data for each file
      //Loop through all these files
      for(var i = 0; i < files.length; i++){
        
        var currentFile = files[i]; 

        if(!currentFile) return; //if no current file, just in case
        const fr = new FileReader();

        fr.onload = (e) => {
          var data = e.target.result.split(','); //split current file
          var fileData = { binary: data[1], mimeType: data[0].match(/:(\w.+);/)[1], filename: currentFile.name };
          innerUploads.push(fileData);
        }
        fr.readAsDataURL(currentFile);
      } //CLOSES FOR LOOP
      uploads.push(innerUploads);

    } //CLOSES IF
  
  }); //CLOSES FOR EACH LOOP

  google.script.run.saveToDrive(uploads);

} //CLOSES FUNCTION

This is what the upload interface looks like. upload elements inside table cells


Solution

  • I believe your goal is as follows.

    • You want to retrieve the values from fileInputs and create an array of uploads including the values like [header, '', '', ''] and { binary: data[1], mimeType: data[0].match(/:(\w.+);/)[1], filename: currentFile.name }.

    When I saw your script, I thought that the reason for your current issue is due to that FileReader is run with the asynchronous process. This has already been mentioned in my 1st comment.

    When this is reflected in your script, how about the following modification?

    Modified script:

    Please modify your Javascript as follows.

    function getFiles(file) {
      return new Promise((resolve, reject) => {
        const fr = new FileReader();
        fr.onload = e => {
          const data = e.target.result.split(",");
          const obj = { binary: data[1], mimeType: data[0].match(/:(\w.+);/)[1], fileName: file.name };
          resolve(obj);
        }
        if (file) {
          fr.readAsDataURL(file);
        } else {
          reject("No file");
        }
      });
    }
    
    async function saveFormData() {
      var fileInputs = document.querySelectorAll('tr.result-row td > input.upload');
      // var firstUploadIndex = fileInputs[0].parentElement.parentElement.getAttribute('data-index'); // This is not used in your showing script.
      var uploads = [];
      var ar = Array.from(fileInputs);
      for (var i = 0; i < ar.length; i++) {
        var files = ar[i].files;
        if (files.length == 0) {
          var header = ar[i].getAttribute('data-uploadtype'); //save the header name as reference
          var fileData = [header, '', '', ''];
          uploads.push(fileData);
        } else {
          for (var j = 0; j < ar[i].files.length; j++) {
            var res = await getFiles(ar[i].files[j]).catch(err => console.log(err));
            uploads.push(res);
          }
        }
      }
    
      console.log(uploads); // You can confirm the value in the log.
    
      google.script.run.saveToDrive(uploads);
    }
    
    • Unfortunately, I cannot know your actual HTML. So, I modified your script by guessing your HTML. If an error occurs, please provide the HTML. By this, I would like to confirm it.