Skip to content

Instantly share code, notes, and snippets.

@tanaikech
Last active September 2, 2023 23:01
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save tanaikech/d3e62002e522f9e3f2b35bc56c64b2c9 to your computer and use it in GitHub Desktop.
Save tanaikech/d3e62002e522f9e3f2b35bc56c64b2c9 to your computer and use it in GitHub Desktop.
Uploading File to Google Drive from External HTML without Authorization

Uploading File to Google Drive from External HTML without Authorization

This is a sample script for uploading a file to Google Drive from the external HTML without the authorization. In this case, the client side can be used at the outside of Google. And as the server side, the Web Apps created by Google Apps Script is used.

Usage

Please do the following flow.

1. Create new project of Google Apps Script.

Sample script of Web Apps is a Google Apps Script. So please create a project of Google Apps Script.

If you want to directly create it, please access to https://script.new/. In this case, if you are not logged in Google, the log in screen is opened. So please log in to Google. By this, the script editor of Google Apps Script is opened.

2. Prepare script.

Please copy and paste the following script (Google Apps Script) to the script editor. This script is for the Web Apps.

Server side: Google Apps Script

Please set the folder ID that you want to put the file.

function doPost(e) {
  const folderId = "###"; // Folder ID which is used for putting the file.

  const blob = Utilities.newBlob(
    JSON.parse(e.postData.contents),
    e.parameter.mimeType,
    e.parameter.filename
  );
  const file = DriveApp.getFolderById(folderId || "root").createFile(blob);
  const responseObj = {
    filename: file.getName(),
    fileId: file.getId(),
    fileUrl: file.getUrl(),
  };
  return ContentService.createTextOutput(
    JSON.stringify(responseObj)
  ).setMimeType(ContentService.MimeType.JSON);
}
  • When you modified the script of Web Apps, please redeploy the Web Apps as new version. By this, the latest script is reflected to Web Apps. Please be careful this.

3. Deploy Web Apps.

  1. On the script editor, Open a dialog box by "Publish" -> "Deploy as web app".
  2. Select "Me" for "Execute the app as:".
    • By this, the script is run as the owner.
  3. Select "Anyone, even anonymous" for "Who has access to the app:".
  4. Click "Deploy" button as new "Project version".
  5. Automatically open a dialog box of "Authorization required".
    1. Click "Review Permissions".
    2. Select own account.
    3. Click "Advanced" at "This app isn't verified".
    4. Click "Go to ### project name ###(unsafe)"
    5. Click "Allow" button.
  6. Click "OK".
  7. Copy the URL of Web Apps. It's like https://script.google.com/macros/s/###/exec.
    • When you modified the Google Apps Script, please redeploy as new version. By this, the modified script is reflected to Web Apps. Please be careful this.

4. Upload a file from client side to server side.

Client side: HTML & Javascript

Please set the URL of your Web Apps to the following script.

<form id="form">
  <input name="file" id="uploadfile" type="file" />
  <input id="submit" type="submit" />
</form>

<script>
  const form = document.getElementById("form");
  form.addEventListener("submit", (e) => {
    e.preventDefault();
    const file = form.file.files[0];
    const fr = new FileReader();
    fr.readAsArrayBuffer(file);
    fr.onload = (f) => {
      const url = "https://script.google.com/macros/s/###/exec"; // Please set the URL of Web Apps.

      const qs = new URLSearchParams({
        filename: file.name,
        mimeType: file.type,
      });
      fetch(`${url}?${qs}`, {
        method: "POST",
        body: JSON.stringify([...new Int8Array(f.target.result)]),
      })
        .then((res) => res.json())
        .then(console.log)
        .catch(console.log);
    };
  });
</script>
  • At the client side, when you selected a file from your local PC and push the button, the file is uploaded to your Google Drive by retrieving the data at the Web Apps (server side).

Limitation

In this case, the maximum file size is 50 MB. Because in the current stage, the maximum blob size is 50 MB at Google Apps Script.

References

@Insidiae
Copy link

Hello,

I'm trying to use this script as a reference to upload some JSON data to a Google Drive folder via a fetch/AJAX POST request from a web/mobile app I'm developing.
I have copied the following code from Step 2 into my Google Apps Script editor, doing some slight modifications to fit the needs of my app:

function doPost(e) {
  try {
    const folderId = "###"; // Folder ID which is used for putting the file.

    const blob = Utilities.newBlob(
      // decode base64 file contents
      Utilities.base64Decode(e.parameter.data),
      e.parameter.mimeType,
      e.parameter.filename
    );
    const file = DriveApp.getFolderById(folderId || "root").createFile(blob);
    const responseObj = {
      filename: file.getName(),
      fileId: file.getId(),
      fileUrl: file.getUrl(),
    };

    return ContentService.createTextOutput(
      JSON.stringify(responseObj)
    ).setMimeType(ContentService.MimeType.JSON);
  } catch (error) {
    Logger.log(error);
    return ContentService.createTextOutput(error);
  }
}

I have also deployed the Apps Script project following the settings given in Step 3:

deploy

Now I am testing the deployed Apps Script using Postman, setting the appropriate filename, mimeType, and file contents (encoded in base64) on the request parameters to simulate a fetch/AJAX POST request on my deployed Apps Script, and I keep getting a 401 Unauthorized response when I try to send the request:

postman-test

For some reason, that "Sorry, unable to open the file at this time" message appears whenever I try to send a POST request without logging in to my Google account (which currently owns the Google Drive folder I want to upload the files on). When I send the same POST request with my Google account logged in, the request is successful and I can see the file being uploaded to my Google Drive folder.

I think the current issue with my Apps Script is that it checks if my Google account is logged in, even though I have deployed the Apps Script to be usable by anyone (ideally without having to log in to a Google account as well). Do I need to set another deployment option to let other users upload to my Google Drive folder without having to log in?


I've also tried to search around for some possible solutions for this, and one interesting result suggested I use the OAuth 2.0 Playground to generate an OAuth Bearer token to authorize(?) my POST request before I can successfully upload the file:

oauth
postman-auth

Setting the Bearer token does get me my desired result, which is to let users upload some JSON data to my Google Drive folder without having to log in.

This current approach seems fine and all, but the problem I see with it is that I have to automatically refresh the OAuth token after some time and modify my POST request using the newly generated OAuth token. Is there a way to do this without having to rewrite my code just to change the OAuth token?

@Insidiae
Copy link

Alright, so I actually switched to the Apps Script Legacy editor to see if the Anyone, even anonymous deploy option would be different from just the Anyone option from the new editor, and it turns out that option from the old editor was the one letting anonymous users run the script and actually upload the files to Google Drive without the need for any Authorization tokens, I guess?

legacy

After deploying with those settings above in the Legacy editor, I finally got my test requests working and actually getting to upload data to Google Drive!

postman

The Apps Script code now runs as expected when I fire off a POST request to my deployment link. Thank you very much for the sample script!

@chgtauhan
Copy link

Hihi, i like your great job.
I am newbie to this.
i actually duplicate your work and change it accordingly. It works.
But, i am facing few problems and i hope you can advise me.

  1. How do i extract the file url and file i from the .then(e=>console.log(e), initially i thought is e is json and when i try to call it, it fails.
  2. how can i ensure the file is uploaded success then only run other code. I try to get response from the fetch but it is not working.

i hope you can help me on this. Thanks in advanced

@Vanlsh
Copy link

Vanlsh commented Nov 30, 2022

Thank you so much, you are the best

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment