javascript 获取函数外部变量的值以创建文件

dxxyhpgq  于 2023-01-24  发布在  Java
关注(0)|答案(2)|浏览(102)

我想知道如何返回createOrGetFolder函数外部的folder变量中创建的文件夹的名称,目的是能够创建与所创建文件夹同名的文件,在以下代码中:

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

这是我的完整代码从.gs文件:

/**
* 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

请注意,在const saveDataAsCSV中,当前文件创建名称为Sample.csv,这就是我要应用函数createOrGetFolder(folderName, parentFolderId)中的folder变量的位置
下面是HTML文件的完整代码:

/**
* 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 class="main" id="form" novalidate="novalidate" style="max-width: 480px;margin: 40px auto;">
 <div id="forminner">
  <h5 class="center-align teal-text" style="margin-bottom: -10px; font-size: 20px; font-family: Rubik; ">YOUR NAME</h5>
    <div class="row">
          <div class="input-field col s12">
            <input id="name01" type="text" name="Name" class="validate" required="required" aria-required="true">
            <label for="name" class="">Name</label>
          </div>
        </div>
    
      <h5 class="center-align teal-text" style="margin-bottom: -10px; font-size: 20px; font-family: Rubik; ">SOME DESCRIPTION</h5>
        <div class="row">
          <div class="input-field col s12">
            <input id="description" type="text" name="Description" class="validate" required="required" aria-required="true">
            <label for="name">Description</label>
          </div>
        </div>

    <div class="row">
      <div class="col-8 col-md-4">
        <h6>Model</h6>       
        <select class="custom-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 class="custom-select" id="Color">
          <option selected="">Choose...</option>
          <option value="Red">Red</option>
          <option value="Green">Green</option>
        </select>   
      </div>
  </div>

    
        <div class="row">
          <div class="col s12">
            <h5 class="center-align teal-text">Upload the Video File</h5>
           
          </div>
        </div>
        
        <div class="row">
          <div class="file-field input-field col s12">
            <div id="input-btn" class="btn">
              <span>File</span>
              <input id="files" type="file" single="">
            </div>
            <div class="file-path-wrapper">
              <input class="file-path validate" type="text" placeholder="Select the file">
            </div>
          </div>
        </div>

        <div class="row">
          <div class="input-field col s6">
            <button id="submit-btn" class="waves-effect waves-light btn submit-btn" type="submit" onclick="submitForm(); return false;">Submit</button>
          </div>
        </div>
        <div class="row">
          <div class="input-field col s12 hide" 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 class="row">
          <div class="input-field col s12" id="progress">
          </div>
        </div>
      </div>
     </div> 
      <div id="success" style="display:none">
        <h5 class="center-align teal-text">Tudo certo!</h5>
        <p>Se você já preencheu todos os campos é só fechar essa janela e clicar em enviar!</p>
        <button id="fechar" class="waves-effect waves-light btn submit-btn" 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>

由于folder没有var前缀,我认为它应该可以工作,因为理论上这使它成为一个全局变量...但是我仍然在控制台中得到folder is undefined消息。
我还尝试在文件创建代码之前调用该函数,如下所示:

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

但这样我得到的信息是folderName is undefined.
根据The WizEd answer's comments中的建议,这是我最后一次尝试:
修改.gs文件中的摘录:

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

修改HTML文件中的摘录:

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);
        });
      }

这样我仍然无法获得创建或使用的文件夹的名称...
我哪里做错了?

pbwdgjma

pbwdgjma1#

全局变量不是持久的,这意味着当一个函数被执行时,示例创建全局变量,但是当函数或函数链结束时释放它。
这里func1()调用func2(),因此全局变量的示例被保留。
但是,如果我在运行func1()之后单独运行func2(),它将重置为空

运行函数1()

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

现在运行func2()

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

要从一次执行到下一次执行保持globalVariable的值,需要使用PropertyService

2q5ifsrm

2q5ifsrm2#

您已使用Google Apps脚本创建了Web应用程序。客户端代码调用服务器端函数createOrGetFolder。您希望此函数返回分配给folder变量的文件夹对象的名称。
当前,服务器端函数createOrGetFolder在成功时返回文件夹id(return folder.getId();)。
要获取文件夹名称,可以使用folder.getName(),但更改此函数的返回值意味着更改客户端代码。
一种方法是添加一个客户端函数来获取文件夹名。这可以通过使用当前返回createOrGetFolder的文件夹id调用服务器端函数来完成。另一种方法是,使createOrGetFolder使用属性服务、该高速缓存服务、或其他存储来保存文件夹名称,然后使用客户端函数检索此值。在这两个选项中,对html /gs文件将是最小的,但这不会提供最佳的整体性能,您的Web应用程序,因为谷歌应用程序脚本服务是缓慢的。
另一种选择是更改createOrGetFolder函数的返回值,但这意味着要花时间研究客户端代码并更改多行代码,可能会比第一种选择花费更多的程序员时间,但可能会保证您的Web应用程序将通过将对Google Apps Script服务的调用保持在最低限度来获得最佳的整体性能。
资源

相关问题