Google Apps 脚本文件上传(通过 HTML 形式)仅在脚本为 "bound" 时有效(测试独立脚本时,HTTP 403 错误)

Google Apps Script file upload (by HTML form) only works when script is "bound" (when standalone script tested, HTTP 403 error)

要将本地文件上传到 Google 驱动器,我有一个 HTML <form></form>(请参阅下面的代码),它在模态顶层显示 Google Sheet 用户已打开。 HTML表单里面有<input type="file" name="...">,点击发送表单对象时,成功上传文件if 这个 Google Apps 脚本是 "bound" 到一个特定的 Sheets 文件(并且是使用 Tools > Script Editor... 菜单编写的)。

如果我将脚本保存为独立脚本,然后在我选择的 Sheets 文件上测试它(已安装并启用),那么 <form> 的 onclick 操作和尝试调用 google.script.run.aServerFunction(...) 会导致 "NetworkError: Connection failure due to HTTP 403"。为了澄清这就是我创建独立脚本并在 Sheets 文件上测试它的意思:https://developers.google.com/apps-script/add-ons/#understand_the_development_cycle。在早期的代码迭代中,我或者得到了某种授权 scriptError。当脚本私下 发布 供测试人员在 Sheet 上使用时出现同样的错误。不幸的是,我想我需要它作为一个独立的脚本,以后可以作为一个附加组件发布——而不是使用 Tools > Script Editor... 菜单绑定到单个 Sheet 的辅助脚本。

我第一次 post Stack Overflow - 请原谅任何行话或排版错误,谢谢!

HTML 改编自教程:

    <!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">
    </script>
    <script>
        function failed(event) {
          $("div.response").text(event);
          //google.script.run.selectStuff();
          //google.script.host.close();
        }
      </script>
  </head>
  <body>
    <form id="myForm">

    <label>Your Name</label>
    <input type="text" name="myName">

    <label>Pick a file</label>
    <input type="file" name="myFile">

    <input type="submit" value="Upload File" 
               onclick="google.script.run.withFailureHandler(failed)
                        .uploadFiles(this.parentNode);
                        return false;">
</form>
<div class="response"></div>
  </body>

</html>

在code.gs:

function uploadFiles(formObject) {
  /*var sheet1 = SpreadsheetApp.getActiveSheet();
  sheet1.setActiveRange(sheet1.getRange(2, 2, 4, 4));

  var formBlob = formObject.myFile;
  var driveFile = DriveApp.createFile(formBlob);
  driveFile.addEditor("...");
  SpreadsheetApp.getActive().toast(driveFile.getUrl());
  sheet1.getRange(1,1,1,1).setValue(driveFile.getUrl());
  return driveFile.getUrl();*/

  return "it worked";
}

我相信您收到 HTTP 403 错误的原因是表单 DOM 元素在 sheet 插件的 google.script.run.myfunction(...) 中是非法参数。尽管它们在这里作为合法参数提到 here,但我认为附加组件具有无法传递任何类型的 DOM 元素的附加限制。

我想出的解决方案是使用本机 javascript 中的 Filereader.readAsDataUrl() 函数将上传的文件转换为 base64 编码字符串,并将该字符串传递给 google 脚本并将其转换回来到要上传到 google 驱动器的文件。 base64 编码字符串开头如下:

data:application/pdf;base64,JVBERi0xLjMKJcTl8uXrp/Og0MTG....

天然气

function uploadFiles(formObject) {
  // extract contentType from the encoded string 
  var contentType = formObject.split(",")[0].split(";")[0].split(":")[1]
  // New Blob(data,contentType,name)
  // Use base64decode to get file data
  var blob = Utilities.newBlob(Utilities.base64Decode(formObject.split(",")[1]), contentType, "trial")
  var driveFile = DriveApp.getFolderById("your Folder ID here").createFile(blob);
  //return contentType, can be anything you like
  return blob.getContentType()
}

HTML 脚本

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">
    </script>

  </head>
  <body>
    <form id="myForm">

    <label>Your Name</label>
    <input type="text" name="myName">

    <label>Pick a file</label>
    <input type="file" id = "filemy" name="myFile">

    <input type="button" value="Upload File" 
               onclick="upload(this.parentNode)">
</form>
<div class="response"></div>
  </body>
  <script>
        function failed(event) {
          $("div.response").text(event);
          //google.script.run.selectStuff();
          //google.script.host.close();
        }

        function upload(frmData){
        var file = document.getElementById("filemy").files[0]
        var reader = new FileReader()
        //reader.onload is triggered when readAsDataURL is has finished encoding
        //This will take a bit of time, so be patient
        reader.onload = function(event) {
        // The file's text will be printed here
        console.log("File being Uploaded")
        //console.log(event.target.result)
        google.script.run.withFailureHandler(failed).withSuccessHandler(failed)
                        .uploadFiles(event.target.result);
        };

        reader.readAsDataURL(file);
        console.log(reader.result)
        }
      </script>

</html>

最后的说明:我没有对代码进行广泛的测试,我已经让它与一个 pdf 文件和一个最大大小为 2.6MB 的 image/png 文件一起工作。因此,请先尝试这两种文件类型,然后再继续使用其他类型的文件! 此外,文件确实需要一段时间才能上传,因此请耐心等待(~5-10 秒)。尤其是没有进度条显示上传进度,感觉就像什么都没有发生一样。

希望对您有所帮助!