appscript.dev
Automation Intermediate Drive

Build a Drive file-upload web app

Let external users send Northwind files via a web form — no Drive account needed.

Published Aug 12, 2025

Northwind regularly needs files from people outside the company — a freelancer’s artwork, a client’s brand guidelines, a contractor’s signed form. Emailing attachments back and forth is messy, and sharing a Drive folder means the other person needs a Google account and the right permissions. Neither is ideal for a one-off handover.

This automation publishes a tiny web app: a single upload form anyone can use with just a link. The file is read in the browser, sent to the script, and dropped straight into a Drive inbox folder Northwind controls. No Google account, no shared-folder permissions — just a link and a file picker.

What you’ll need

  • A Drive folder to act as the upload inbox, and its folder ID from the URL.
  • An Apps Script project containing two files: the server script below and an HTML file named exactly Upload (Apps Script adds the .html itself).
  • Permission to deploy the project as a web app.

The HTML (Upload.html)

<!-- A minimal upload form. The file is read client-side and passed
     to the server function, so no real form POST is needed. -->
<form id="up" enctype="multipart/form-data">
  <input type="file" id="f" required>
  <button>Upload</button>
</form>
<script>
  document.getElementById('up').addEventListener('submit', (e) => {
    // Stop the browser submitting the form the normal way.
    e.preventDefault();

    // Grab the chosen file and read it as a base64 data URL.
    const file = document.getElementById('f').files[0];
    const reader = new FileReader();
    reader.onload = () => {
      // Pass the name, type, and base64 body to the server function.
      google.script.run.handleUpload({
        name: file.name,
        mimeType: file.type,
        b64: reader.result.split(',')[1],
      });
    };
    reader.readAsDataURL(file);
  });
</script>

The script

// The Drive folder uploaded files are dropped into.
const UPLOAD_INBOX_ID = '1abcUploadInboxId';

/**
 * Serves the upload form when the web app URL is opened.
 * @return {HtmlOutput} The rendered Upload.html page.
 */
function doGet() {
  return HtmlService.createHtmlOutputFromFile('Upload');
}

/**
 * Receives a file from the upload form and saves it to the inbox folder.
 * @param {{name: string, mimeType: string, b64: string}} payload
 *   The file's name, MIME type, and base64-encoded body.
 */
function handleUpload({ name, mimeType, b64 }) {
  // Guard against an empty or malformed submission.
  if (!name || !b64) {
    throw new Error('Upload is missing a file name or content.');
  }

  // Rebuild the file from its base64 body and save it to the inbox.
  const blob = Utilities.newBlob(Utilities.base64Decode(b64), mimeType, name);
  const file = DriveApp.getFolderById(UPLOAD_INBOX_ID).createFile(blob);
  Logger.log(`Saved upload: ${file.getName()}`);
}

How it works

  1. When someone opens the web app URL, doGet runs and returns the Upload HTML file — the browser shows the file picker and button.
  2. The user chooses a file and clicks Upload. The page’s script intercepts the form submit so the browser does not navigate away.
  3. A FileReader reads the chosen file as a base64 data URL. The script splits off the data:...;base64, prefix, keeping just the encoded body.
  4. google.script.run.handleUpload(...) calls the server function, passing the file’s name, MIME type, and base64 content as a plain object.
  5. On the server, handleUpload checks the payload has a name and content, then throws early if it does not.
  6. Utilities.base64Decode turns the string back into bytes, Utilities.newBlob wraps them as a file blob, and DriveApp creates the file in the inbox folder.

Example run

After deploying, you share the web app URL with an external contributor. They open it and see:

[ Choose file ]  [ Upload ]

They pick brand-guidelines.pdf and click Upload. A moment later the file appears in Northwind’s upload inbox folder in Drive, owned by the script’s account — the contributor never needed a Google login or any folder access.

Run it

This automation runs as a deployed web app:

  1. In the Apps Script editor, click Deploy → New deployment.
  2. Choose Web app as the type.
  3. Set Execute as to Me (so files are created by your account) and Who has access to Anyone (so external users can reach it).
  4. Click Deploy, approve the authorisation prompt, and copy the web app URL.
  5. Share that URL with whoever needs to send you a file.

Watch out for

  • google.script.run caps payloads at around 50 MB, and a base64 string is about a third larger than the file itself — so the practical limit is well under 50 MB. For big files, have users upload to a Drive folder you share.
  • Setting Who has access to Anyone means the URL is genuinely public. Anyone with the link can upload, so treat the inbox folder as untrusted and scan what arrives.
  • There is no feedback in this minimal form — the user sees nothing after they click. Add a success or error message via withSuccessHandler / withFailureHandler for a usable experience.
  • The whole file is held in memory as base64 on both the client and server. Very large files can exhaust the script’s memory even below the payload cap.
  • Each time you change the code, create a new deployment version (or update the existing one) — otherwise the live URL keeps serving the old code.

Related