2

To upload a local file to Google Drive, I have an HTML <form></form> (see code below) that displays in a modal overtop a Google Sheet the user has open. The HTML form has <input type="file" name="..."> in it, and when I click to send the form object, I successfully upload the file if this Google Apps Script is "bound" to a specific Sheets file (and was written using the Tools > Script Editor... menu).

If I save the script as a standalone script and then test it (installed and enabled) on a Sheets file of my choosing, then the <form>'s onclick action and the attempt to call google.script.run.aServerFunction(...) causes a "NetworkError: Connection failure due to HTTP 403". To clarify this is what I mean by creating a standalone script and testing it on a Sheets file: https://developers.google.com/apps-script/add-ons/#understand_the_development_cycle. In earlier code iterations I alternatively got a authorization scriptError of some kind. Same error when script is published privately for testers to use on a Sheet. Unfortunately, I think I need this as a standalone script that is later publishable as an add-on- not a side script bound to a single Sheet using the Tools > Script Editor... menu.

My first post to Stack Overflow- please forgive any jargon or typography mistakes, and thank you!

HTML adapted from tutorials:

    <!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>

In the 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";
}
6
  • 1
    You can't use getActiveSheet() from a script that is not bound to the spreadsheet. You have: var sheet1 = SpreadsheetApp.getActiveSheet(); You will need to get the spreadsheet file by ID. It's pointless to get it by URL unless that's the only thing available to use. Commented Apr 2, 2017 at 16:43
  • Can you explain how you do this:"If I save the script as a stand-alone script and then test it (installed and enabled) on a Sheets file of my choosing." Are your trying to create a sheet add on? If you are creating a standalone script are you using doGet() to serve the HTML page? Commented Apr 2, 2017 at 17:28
  • @SandyGood Thanks very valid point. But did not fix the "NetworkError: Connection failure due to HTTP 403" - the failure handler in the HTML's onclick still fires. I've changed the code above to remove getActiveSheet() Commented Apr 3, 2017 at 0:14
  • @JackBrown I'm trying to create a Sheet add-on. It says here I can do that by writing a standalone script, which is later tested on specific Sheets and can be published as an add-on: (developers.google.com/apps-script/add-ons/…). I am using instructions at (developers.google.com/apps-script/add-ons/test#top_of_page) to "Verify that an add-on written in a standalone script functions ... when applied to a Sheet". I also published privately. No doGet() is used -I use SpreadsheetApp.getUi().showModalDialog(htmlOutputObj, 'Upload Student Roster');. Commented Apr 3, 2017 at 0:31
  • Since your using the script as sheet add on, you can use 'getactive()' to get the spreadsheet. And your running the test add on as installed and enabled, so authorization should not be a issue. I am at a loss here :/. I will give it a shot and see if I can reproduce the error and hope some else is able to give you a better suggestion than I did. Commented Apr 3, 2017 at 0:58

1 Answer 1

3

I believe the reason you are getting the HTTP 403 error is because form DOM elements are illegal arguments in google.script.run.myfunction(...) in sheet addons. Even though they are mentioned here as legal parameters here, I think add-on have the added restriction of not being able to pass any kind of DOM elements.

The solution I came up with is to convert the uploaded file to base64 encode string using Filereader.readAsDataUrl() function in native javascript and passing the string to google script and converting it back to a file to be uploaded into google drive. The base64 encode string starts like this:

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

GAS

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 script

<!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>

Final notes: I have not extensively tested the code, I have got it to work with a pdf file and an image/png file with a Maximun size of 2.6MB. So please try these 2 file types out before going on to further types of file! Also, files do take a while to upload so be patient(~5-10sec). Especially since there is no progress bar to show the upload progress, it feels like nothing is happening.

Hope that helps!

Sign up to request clarification or add additional context in comments.

1 Comment

I will try this tomorrow- but you got it working on a pdf and png image which is more than I managed so should work! Thanks so much!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.