Home > OS >  Get the value of the variable outside the function to create a file
Get the value of the variable outside the function to create a file

Time:01-23

I would like to know how I can return the name of the folder created in the folder variable outside the createOrGetFolder function, the intention is to be able to create a file with the same name as the folder created, in this code here:

const saveDataAsCSV = (data, folderId) => DriveApp.getFolderById(folderId).createFile(folder, data);

This is my complete code from the .gs file:

/**
* Modified script written by Tanaike and CharlesPlucker
*
* Additional Script by Tyrone
* version 20.01.2023.1
*/

        function doGet(e) {
          return HtmlService.createTemplateFromFile('forms0101.html').evaluate();
        }
        
        
        function getOAuthToken() {
          return ScriptApp.getOAuthToken();
        }
        
        
        function getParent(){
          var ss = SpreadsheetApp.getActiveSpreadsheet();
          var id = ss.getId();
          var parent = DriveApp.getFileById(id).getParents().next().getId();
          return parent
        }
        
        function getLimitFolder(){
          var ss = SpreadsheetApp.getActiveSpreadsheet();
          var pastapai = DriveApp.getFileById(ss.getId()).getParents();
          var limitfolder = pastapai.next().getFoldersByName("_").next().getId();
          return limitfolder
        }
        
        /**
        * creates a folder under a parent folder, and returns it's id. If the folder already exists
        * then it is not created and it simply returns the id of the existing one
        */
        
        function createOrGetFolder(folderName, parentFolderId) {
          try {
            var parentFolder = DriveApp.getFolderById(parentFolderId), folder;
            if (parentFolder) {
              var foldersIter = parentFolder.getFoldersByName("Video");
              if (foldersIter.hasNext()) {
                var videoFolder = foldersIter.next();
                var nextFolderName = folderName   "-01";
                while (!folder) {
                  video_folder = videoFolder.getFoldersByName(nextFolderName);
                  if (video_folder.hasNext()) {
                    folder = video_folder.next();
                    var files = folder.getFiles();
                    if (files.hasNext()) {
                      var [a, b] = nextFolderName.split("-");
                      nextFolderName = `${a}-${String(Number(b)   1).padStart(2, "0")}`;
                      folder = null;
                    }
                  } else {
                    folder = videoFolder.createFolder(nextFolderName);
                  }
                }
              } else {
                folder = parentFolder.createFolder("Video");
                folder = folder.createFolder(folderName);
              }
            } else {
              throw new Error("Parent Folder with id: "   parentFolderId   " not found");
            }
            return folder.getId();
          } catch (error) {
            return error;
          }
        }
        
        const saveDataAsCSV = (data, folderId) => DriveApp.getFolderById(folderId).createFile("Sample.csv", data);
    
    
    // NOTE: always make sure we use DriveApp, even if it's in a comment, for google to import those
    // libraries and allow the rest of the app to work. see https://github.com/tanaikech/Resumable_Upload_For_WebApps

Note that in const saveDataAsCSV the currently file creation name is Sample.csv, and this is where I want to apply the folder variable that is in the function createOrGetFolder(folderName, parentFolderId)

And this is the complete code of the HTML file:

/**
* Modified script written by Tanaike and CharlesPlucker
*
* Additional Script by Tyrone
* version 20.01.2023.1
*/

<!DOCTYPE html>
<html>
  <head>
    <base target="_blank">
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Drive Multi Large File Upload</title>
    <link href="https://fonts.googleapis.com/icon?family=Material Icons" rel="stylesheet">

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.5/css/materialize.min.css">
     
     <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA 058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

    <style>
      @import url('https://fonts.googleapis.com/css2?family=Rubik:wght@400;600;700&display=swap');
      
      .disclaimer{
        width: 480px; 
        color: #646464;
        margin: 20px auto;
        padding:0 16px;
        text-align:center;
        font:400 12px Rubik,sans-serif;
        }

      h5.center-align.teal-text {
        font:700 26px Rubik,sans-serif;
        color: #00F498!important;
      }

      .row {
        font:600 14px Rubik,sans-serif;
      }

      .btn {
        background-color: black;
      }

      .btn:hover {
        background-color: #00F498;
      }

      body {
        margin-top: -40px;
      }

      #progress {
        color: #00000;
      }
      
      .disclaimer a{
        color: #00BCAA;
        }

      #credit{
        display:none
        }
    </style>
  </head>
  <body>
    <form  id="form" novalidate="novalidate" style="max-width: 480px;margin: 40px auto;">
 <div id="forminner">
  <h5  style="margin-bottom: -10px; font-size: 20px; font-family: Rubik; ">YOUR NAME</h5>
    <div >
          <div >
            <input id="name01" type="text" name="Name"  required="required" aria-required="true">
            <label for="name" >Name</label>
          </div>
        </div>
    
      <h5  style="margin-bottom: -10px; font-size: 20px; font-family: Rubik; ">SOME DESCRIPTION</h5>
        <div >
          <div >
            <input id="description" type="text" name="Description"  required="required" aria-required="true">
            <label for="name">Description</label>
          </div>
        </div>

    <div >
      <div >
        <h6>Model</h6>       
        <select  id="Model">
          <option selected="">Choose...</option>
          <option value="01">01</option>
          <option value="02">02</option>
          <option value="03">03</option>
        </select> 
          &nbsp;

        <h6>Color</h6>        
        <select  id="Color">
          <option selected="">Choose...</option>
          <option value="Red">Red</option>
          <option value="Green">Green</option>
        </select>   
      </div>
  </div>

    
        <div >
          <div >
            <h5 >Upload the Video File</h5>
           
          </div>
        </div>
        
        <div >
          <div >
            <div id="input-btn" >
              <span>File</span>
              <input id="files" type="file" single="">
            </div>
            <div >
              <input  type="text" placeholder="Select the file">
            </div>
          </div>
        </div>

        <div >
          <div >
            <button id="submit-btn"  type="submit" onclick="submitForm(); return false;">Submit</button>
          </div>
        </div>
        <div >
          <div  id="update">
            <hr>
            <p>
              Por favor, aguarde enquanto seu arquivo está sendo carregado.<br><span style="color: #00000;"><b>Não feche ou atualize a janela durante o upload.</b></span>
            </p>
          </div>
        </div>
        <div >
          <div  id="progress">
          </div>
        </div>
      </div>
     </div> 
      <div id="success" style="display:none">
        <h5 >Tudo certo!</h5>
        <p>Se você já preencheu todos os campos é só fechar essa janela e clicar em enviar!</p>
        <button id="fechar"  style ="transform: translateX(160%);" type="button" onclick="google.script.host.close()">Fechar</button>
      </div>
    </form>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.5/js/materialize.min.js"></script>
    <script src="https://gumroad.com/js/gumroad.js"></script>

    <script>

      var upload_folder = "01";
      const chunkSize = 5242880;
      const uploadParentFolderId = <?=getParent()?>; // creates a folder inside of this folder
      const limitfolder = <?=getLimitFolder()?>;

      function closer(){
        google.script.host.close();
      }

      function submitForm() {

        // Added the below script.
        if ($('#submit-btn.disabled')[0]) return; // short circuit

        var name = upload_folder
        var files = [...$('#files')[0].files]; // convert from FileList to array

        
        if (files.length === 0) {
          showError("Por favor, selecione um arquivo");
          return;
        }

        var name = $('#name01').val();
        var description = $('#description').val();
        var model = $('#Model').val();
        upload_folder = model;
        var color = $('#Color').val();
        var form_values = [name, description, model, color];
        form_values = form_values.map(r => r.replaceAll(",", "#")); // Essa linha substitui todas as "," por "#" antes de gerar o .csv
        var data = form_values.join(",");
        google.script.run.saveDataAsCSV(data, uploadParentFolderId);
        google.script.run.saveDataAsCSV(data, limitfolder);

        disableForm(); // prevent re submission

        // the map and reduce are here to ensure that only one file is uploaded at a time. This allows
        // the promises to be run sequentially
        files.map(file => uploadFilePromiseFactory(file))
            .reduce((promiseChain, currentTask) => {
              return promiseChain.then(currentTask);
            }, Promise.resolve([])).then( () => {
              console.log("Completed all files upload");
              showSuccess();
            });
      }

      function disableForm() {
        $('#submit-btn').addClass('disabled');
        $('#input-btn').addClass('disabled');
        $('#update').removeClass('hide');
         $('#update').removeClass('hide');
      }

      function uploadFilePromiseFactory(file) {
        return () => {
          console.log("Processing: ", file.name);
          return new Promise((resolve, reject) => {

            showProgressMessage("Seu arquivo está sendo carregado");

            var fr = new FileReader();
            fr.fileName = file.name;
            fr.fileSize = file.size;
            fr.fileType = file.type;
            // not sure of a better way of passing the promise functions down
            fr.resolve = () => resolve();
            fr.reject = (error) => reject(error);
            fr.onload = onFileReaderLoad;
            fr.readAsArrayBuffer(file);
          });
        };
      }
      
      /**
       * Gets called once the browser has loaded a file. The main logic that creates a folder
       * and initiates the file upload resides here
       */
      function onFileReaderLoad(onLoadEvent) {
        var fr = this;

        var newFolderName = upload_folder
        createOrGetFolder(newFolderName, uploadParentFolderId).then(newFolderId => {
          console.log("Found or created guest folder with id: ", newFolderId);
          uploadFileToDriveFolder.call(fr, newFolderId).then(() => {
              fr.resolve();
            }, (error) => {
              fr.reject(error);
            });
          },
          (error) => {
            if (error) {
              showError(error.toString());
            }
            console.log("onFileReaderLoad Error2: ", error);
          });

      }

      /**
       * call to the DriveApp api. Wrapped in a promise in case I want to address timing issues between a
       * createFolder and findFolderById
       */
      function createOrGetFolder(folderName, parentFolderId) {
        return new Promise((resolve, reject) => {
          google.script.run.withSuccessHandler(response => {
            console.log("createOrGetFolder response: ", response);
            if (response && response.length) {
              resolve(response);
            }
            reject(response);
          }).createOrGetFolder(folderName, parentFolderId);
        });
      }

      /**
      * Helper functions modified from:
      * https://github.com/tanaikech/Resumable_Upload_For_WebApps
      */
      function uploadFileToDriveFolder(parentFolderId) {
        var fr = this;
        return new Promise((resolve, reject) => {
          var fileName = fr.fileName;
          var fileSize = fr.fileSize;
          var fileType = fr.fileType;
          console.log({fileName: fileName, fileSize: fileSize, fileType: fileType});
          var buf = fr.result;
          var chunkpot = getChunkpot(chunkSize, fileSize);
          var uint8Array = new Uint8Array(buf);
          var chunks = chunkpot.chunks.map(function(e) {
            return {
               data: uint8Array.slice(e.startByte, e.endByte   1),
               length: e.numByte,
               range: "bytes "   e.startByte   "-"   e.endByte   "/"   chunkpot.total,
            };
          });
          google.script.run.withSuccessHandler(oAuthToken => {
            var xhr = new XMLHttpRequest();
            xhr.open("POST", "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable");
            xhr.setRequestHeader('Authorization', "Bearer "   oAuthToken);
            xhr.setRequestHeader('Content-Type', "application/json");
            xhr.send(JSON.stringify({
              mimeType: fileType,
              name: fileName,
              parents: [parentFolderId]
            }));
            xhr.onload = () => {
              doUpload(fileName, {
                location: xhr.getResponseHeader("location"),
                chunks: chunks,
              }).then(success => {
                resolve(success);
                console.log("Successfully uploaded: ", fileName);
              },
              error => {
                reject(error);
              });
            };

            xhr.onerror = () => {
              console.log("ERROR: ", xhr.response);
              reject(xhr.response);
            };
          }).getOAuthToken();
        });
      }

      function showSuccess() {
        $('#forminner').hide();
        $('#success').show();
        $('#fechar').show();
      }

      function showError(e) {
        $('#progress').addClass('red-text').html(e);
      }

      function showMessage(e) {
        $('#update').html(e);
      }

      function showProgressMessage(e) {
        $('#progress').removeClass('red-text').html(e);
      }

      /**
      * Helper functions modified from:
      * https://github.com/tanaikech/Resumable_Upload_For_WebApps
      */
      function doUpload(fileName, e) {
        return new Promise((resolve, reject) => {
          showProgressMessage("Carregando: <span style='color: #00F498 ;'>"   "0%</span>");
          var chunks = e.chunks;
          var location = e.location;
          var cnt = 0;
          var end = chunks.length;
          var temp = function callback(cnt) {
            var e = chunks[cnt];
            var xhr = new XMLHttpRequest();
            xhr.open("PUT", location, true);
            console.log("content range: ", e.range);
            xhr.setRequestHeader('Content-Range', e.range);
            xhr.send(e.data);
            xhr.onloadend = function() {
                var status = xhr.status;
                cnt  = 1;
                console.log("Uploading: "   status   " ("   cnt   " / "   end   ")");
                showProgressMessage("Carregando: <span style='color: #00F498 ;'>" 
                         Math.floor(100 * cnt / end)   "%</span>" );
                if (status == 308) {
                    callback(cnt);
                } else if (status == 200) {
                    $("#progress").text("Done.");
                    resolve();
                } else {
                    $("#progress").text("Error: "   xhr.response);
                    reject();
                }
            };
          }(cnt);
        });
      }

      /**
      * Helper functions modified from:
      * https://github.com/tanaikech/Resumable_Upload_For_WebApps
      */
      function getChunkpot(chunkSize, fileSize) {
        var chunkPot = {};
        chunkPot.total = fileSize;
        chunkPot.chunks = [];
        if (fileSize > chunkSize) {
            var numE = chunkSize;
            var endS = function(f, n) {
                var c = f % n;
                if (c == 0) {
                    return 0;
                } else {
                    return c;
                }
            }(fileSize, numE);
            var repeat = Math.floor(fileSize / numE);
            for (var i = 0; i <= repeat; i  ) {
                var startAddress = i * numE;
                var c = {};
                c.startByte = startAddress;
                if (i < repeat) {
                    c.endByte = startAddress   numE - 1;
                    c.numByte = numE;
                    chunkPot.chunks.push(c);
                } else if (i == repeat && endS > 0) {
                    c.endByte = startAddress   endS - 1;
                    c.numByte = endS;
                    chunkPot.chunks.push(c);
                }
            }
        } else {
            var chunk = {
                startByte: 0,
                endByte: fileSize - 1,
                numByte: fileSize,
            };
            chunkPot.chunks.push(chunk);
        }
        return chunkPot;
      }

    </script>

  </body>

</html>

As folder is without the var prefix, I figured it should work, as in theory this makes it a global variable... however I still get the folder is undefined message in the console.

I also tried calling the function before the file creation code, like this:

createOrGetFolder(folderName, parentFolderId);
const saveDataAsCSV = (data, folderId) => DriveApp.getFolderById(folderId).createFile(folder, data);

But that way I get the message folderName is undefined.


Based on the suggestion made in the The WizEd answer's comments, this was my last attempt:

Modified excerpt in the .gs file:

const saveDataAsCSV = (data, folderId) => DriveApp.getFolderById(folderId).createFile(newFolderId, data);

Modified excerpt in the HTML file:

       var newFolderId = "";
      /**
       * call to the DriveApp api. Wrapped in a promise in case I want to address timing issues between a
       * createFolder and findFolderById
       */
      function createOrGetFolder(folderName, parentFolderId) {
        return new Promise((resolve, reject) => {
          google.script.run.withSuccessHandler(response => {
            console.log("createOrGetFolder response: ", response);
            if (response && response.length) {
              resolve(response);
            }
            reject(response);
          }).createOrGetFolder(folderName, parentFolderId);
          newFolderId = createOrGetFolder(folderName, parentFolderId);
        });
      }

That way I still can't get the name of the folder created or used...

Where am I going wrong?

CodePudding user response:

Global variable are not persistent. What that means is when a function is executed the instance creates the global variable but releases it when the function or function chain finishes.

Here func1() calls func2() so the instance of the global variable is perserved.

However if I run func2() by itself following running func1() it is reset to blank

Run func1()

var globalVariable = "";

function func1 () {
  try {
    console.log("globalVariable in func1 = [" globalVariable "]")
    globalVariable = "Hello";
    func2();
    console.log("globalVariable from func2 = [" globalVariable "]")
  }
  catch(err) {
    console.log("Error in func1: " err);
  }
}

function func2 () {
  try {
    console.log("globalVariable in func2 = [" globalVariable "]")
    globalVariable = "Good bye";
  }
  catch(err) {
    console.log("Error in func2: " err);
  }
}

11:53:15 AM Notice  Execution started
11:53:17 AM Info    globalVariable in func1 = []
11:53:17 AM Info    globalVariable in func2 = [Hello]
11:53:17 AM Info    globalVariable from func2 = [Good bye]
11:53:16 AM Notice  Execution completed

Now run func2()

11:57:38 AM Notice  Execution started
11:57:39 AM Info    globalVariable in func2 = []
11:57:39 AM Notice  Execution completed

To perserve the value of globalVariable from one execution to the next you need to use PropertyService

CodePudding user response:

You have created a web application using Google Apps Script. The client-side code calls the server side function createOrGetFolder. You want that this function returns the name of the Folder object assigned to the folder variable.

Currently the server side function createOrGetFolder on success returns the folder id (return folder.getId();).

To get the folder name you could use folder.getName() but changing the return of this function implies to make changes to the client-side code.

One option is to add a a client function to get the folder name. This could be done by calling a server side function using the folder id that currently returns createOrGetFolder. Another way is, to make that createOrGetFolder store the folder name using the Properties Service, the Cache Service, or other store to save the folder name then using a client side function retrieve this value. In both options is very likely that the changes to the html / gs files will be minimal but this will not deliver an optimal overall performance of your web application as Google Apps Script services are slow.

Another option is to change the createOrGetFolder function return but that implies investing time on studying the client side code and changing multiple lines of code, probably will be more expensive in programmer hours than the first option but might warrant that your web application will have an optimal overall permorance by keeping the calls to the Google Apps Script services at minimum.

  • Related