DEV Community

Justin Poehnelt for Google Workspace Developers

Posted on • Originally published at justin.poehnelt.com on

Drive File Get Blob and Scopes in Google Apps Script

Apps Script is often a convenient way to interact with Google Drive files. However, there are challenges when working with binary files like PDFs or images, specifically related to scopes.

These challenges arise from the different authorization patterns:

  • Running a script manually : any scope can be used.
  • Running a script or function bound to a Google Workspace document : any scope can be used, but it is recommended to minimize the required scopes.
  • Running a script as a Workspace Add-on : the minimal scope must be used, and restricted scopes like https://www.googleapis.com/auth/drive should be avoided.

Everything below assumes that you are in one of the latter scenarios and are trying to limit restricted and sensitive scopes.

I encountered this issue while working on my Google Cloud Next talk and a demo involving a Workspace Add-on to compress images. Although my use case was related to images, the same issue applies to PDFs and obtaining the Blob of the file. But first, let’s provide some background information.

Methods to obtain the Blob of a file in Google Drive

There are three main methods to obtain the Blob of a file in Google Drive:

DriveApp

This method is the most straightforward, but it requires the drive scope.

DriveApp.getFileById(id).getBlob()
Enter fullscreen mode Exit fullscreen mode

Drive Advanced Service

This method, which uses the Drive Advanced Service, works with the drive.file, but doesn’t work with alt=media. See this issue in the Google Issue Tracker.

// This will throw an error
Drive.Files.get(id, {alt: "media"}) 
Enter fullscreen mode Exit fullscreen mode

UrlFetchApp

This method is the most flexible, but it requires the script.external_request scope. While this allows retrieving a single file from Drive as a Blob, it also allows sending requests to any URL, which poses a potential security risk. This method is likely the best option for getting through the OAuth verification process for your Workspace Add-on, but it may not be installed by some organizations with strict data loss prevention policies.

const url = `https://www.googleapis.com/drive/v3/files/${id}?alt=media`;
UrlFetchApp.fetch(url, {
headers: {
    Authorization: `Bearer ${ScriptApp.getOAuthToken()}`,
},
}).getContent()
Enter fullscreen mode Exit fullscreen mode

Code snippets for each method and comparison

Here are the code snippets for each method and a comparison of the first 10 bytes of the Blob obtained by each method. Replace ID with the ID of the file you want to test.

const ID = "18q95H4slpt6sgtkbEoq6m9rppCb-GAEX";
function getBlobById(id = ID) {
  return DriveApp.getFileById(id).getBlob();
}
function getBlobByIdAdvanced(id = ID) {
  // Does not work with alt: "media"
  try {
    return Drive.Files.get(id, { alt: "media" });
  } catch (e) {
    console.error(e.message);
  }
}
function getBlobByUrl(id = ID) {
  const url = `https://www.googleapis.com/drive/v3/files/${id}?alt=media`;
  return UrlFetchApp.fetch(url, {
    headers: {
      // This token will differ based upon the context of the script execution
      Authorization: `Bearer ${ScriptApp.getOAuthToken()}`,
    },
  }).getContent();
}
function test() {
  const driveAppBlob = getBlobById(ID);
  const driveAdvancedBlob = getBlobByIdAdvanced(ID);
  const urlFetchBlob = getBlobByUrl(ID);
  console.log({
    driveAppBytes: driveAppBlob.getBytes().slice(0, 10),
    driveAdvancedBytes: driveAdvancedBlob?.getBytes()?.slice(0, 10),
    urlFetchBlobBytes: urlFetchBlob.slice(0, 10),
  });
}
Enter fullscreen mode Exit fullscreen mode

The test() function will log the first 10 bytes of the Blob obtained by each method. You can run this function from the Apps Script editor to compare the results.

9:52:12 AM    Notice  Execution started
9:52:13 AM    Error   Failed with: Drive.Files.get(id, { alt: "media" })
9:52:14 AM    Info    {
  driveAppBytes:     [-119, 80, 78, 71, 13, 10, 26, 10, 0, 0],
  driveAdvancedBytes: undefined,
  urlFetchBlobBytes: [-119, 80, 78, 71, 13, 10, 26, 10, 0, 0] }
9:52:14 AM    Notice  Execution completed
Enter fullscreen mode Exit fullscreen mode

Top comments (0)