In Using Drive SDK I described why you might want to use the SDK JSON API rather than the built in Apps Script Drive Class. Before you can start with this you need to authorize your access with OAUTH2, as all API requests need an access token. This one off exercise is straightforward and described here.
This page describes how to use the API from apps script in some detail. Nowadays there is built in access to Advanced Drive Service (this is not the same as the regular DriveApp service). All you have to is enable it in Advanced Services and the cloud console and authorize it, but this library uses the JSON API directly.
The API can be a little intimidating and complicated, so I've created a library with most of the functions you'll need to do basic operations directly with the API. A couple of concepts to start. Results ObjectThis is a standard format I return from most operations. It looks like this { success: boolean, // whether the operation succeeeded data: object, // the Parsed data returned from the API code: number, // the HTTP response code url: string, // the constructed url that was used extended: string, // any additional debugging information I've inserted content:string // the unparsed content returned from the API }; FieldsLeft to its own devices the API returns an awful lot of stuff, most of which you don't need. It provides a method of asking for partial responses. The methods I have implemented that return data from the API all accept an options parameter that can be used to limit the returned data to only what's needed. I recommend you use it.FoldersDrive seems to be organized around a flat system of linked IDS rather than the hierarchical structure we are used to with most file systems. Folder structures are emulated through linking to parents and children. You'll see that this library provides methods for working with folder paths, which get translated into drive structures behind the scenes.Metadata versus dataMost of the documentation here refers to the meta data associated with a file rather than the data itself. Strangely I took a while to actually find out how to do this. I've implemented simple uploading and getting, which you'll find documented close to the end of this page. Getting startedOnce you have set up your oAuth2 environment, you are ready to go. In the documentation below, I'll show examples of how to use, and also sometimes show the code in the library for information. The full code of the latest library will be shown at the end, so there may have been some changes since I initially wrote this.Getting a handleIt's just as well to set up the accessToken right from the start when you take out the handlevar dapi = new c DriveJsonApi. DriveJsonApi().setAccessToken(accessToken);
However, this will return a huge amount of stuff, so if you know which fields you want, its better to say. This applies to all methods in this documentation that take an optFields argument. I wont repeat this for each one.
library code /** * given a fileID, return its info * @param {string} id * @param {string} optFields * @return {object} the response */ self.getFileById = function (id,optFields) { return self.urlGet(self.apiBase() + "/" + id + self.joinParams(self.fields(optFields))); }; Get the root folder's metadataFolders in Drive are not really different than files, except for MimeType. var root = dapi.getRootFolder(); library code
/** * get the root folder * @param {string} optFields the fields to return * @return {object} standard results object */ self.getRootFolder = function (optFields) { return self.getFileById('root', optFields); }; ENUMSI provide various useful ENUMS that can be access via
var enums = dapi.getEnums ();library code
var ENUMS = { MIMES: { SOURCE:"application/vnd.google-apps.script+json", SCRIPT:"application/vnd.google-apps.script", FOLDER:"application/vnd.google-apps.folder", AUDIO:"application/vnd.google-apps.audio", DOCUMENT:"application/vnd.google-apps.document", DRAWING:"application/vnd.google-apps.drawing", FILE:"application/vnd.google-apps.file", FORM:"application/vnd.google-apps.form", PHOTO:"application/vnd.google-apps.photo", PRESENTATION:"application/vnd.google-apps.presentation", SITES:"application/vnd.google-apps.sites", FUSIONTABLE:"application/vnd.google-apps.fusiontable", SPREADSHEET:"application/vnd.google-apps.spreadsheet", UNKNOWN:"application/vnd.google-apps.unknown", VIDEO:"application/vnd.google-apps.video" } }; Get a collection of items that match a queryThe API provides a comprehensive searching mechanism as described here. This query returns just the ids of all scripts in my drive that are owned by me
library code
/** * do an api query * @param {string} qString the query string * @param {string} optFields a list of fields to include in the response(if missing then all fields) * @return {object} a standard results object */ self.query = function (qString,optFields) { // apend constraint to exclude delted files qString += ((qString ? " and " : "") + "trashed=false"); // do the query return self.urlGet( this.apiBase() + self.joinParams(["q=" + encodeURIComponent(qString), self.fields(optFields)])); }; Get a collection of child foldersGetting a collection of child folders is just a specialized form of query which includes the parentId in its url.
library code /** * get child folders * @param {string} parentId the id of the parent * @param {string} optFields the fields to return * @param {Array.string} optExtraQueries */ self.getChildFolders = function (parentId,optFields,optExtraQueries) { return self.getChildItems(parentId , self.getEnums().MIMES.FOLDER , optFields, optExtraQueries) ; }; Get a collection of folders by nameGetting a folder by name is just an additional query filter on getting a list of child folders.
library code /** * get folders by name * @param {string} parentId the parentId * @param {string} name * @param {string} optFields the fields to return */ self.getFoldersByName = function (parentId, name , optFields) { return self.getChildFolders (parentId, optFields , "title='" + name + "'"); }; Getting or creating a folder id by pathIf you are planning to organize by folders, which most of us still do, this is probably the most useful method, since it takes a path like /abc/def/ghi and retrieves the id of 'ghi'. Optionally it will also take care of creating 'abc' and 'def' and 'ghi' if they don't already exist. get folder id or null
get folder id, and create folders if needed
library code /** * return a folder id from a path like /abc/def/ghi * @param {string} path the path * @param {boolean} optCreate if true, then create it if it doesnt exist * @return {object} {id:'xxxx'} or null */ self.getFolderFromPath = function (path,optCreate) { return (path || "/").split("/").reduce ( function(prev,current) { if (prev && current) { // this gets the folder with the name of the current fragment var fldrs = self.getFoldersByName(prev.id,current,"items(id)"); // see if it existed var f = fldrs.success && fldrs.data.items.length ? fldrs.data.items[0] : null; // if not then create it. if (!f && optCreate) { // create it and return the id of created folder var r = self.createFolder(prev.id , current,"id"); if(r.success && r.data) { f = r.data; } } return f; } else { return current ? null : prev; } },self.getRootFolder("id").data); };
library code /** * create a folder * @param {string} parentId the folder parent id * @param {string} name the filename * @param {string} optFields optional return fields * @return }object} a standard result object */ self.createFolder = function (parentId, name,optFields) { return self.createItem(parentId , name , self.getEnums().MIMES.FOLDER, optFields); };
library code /** * create an item * @param {string} parentId the folder parent id * @param {string} name the filename * @param {string} mime the mimetype, * @param {string} optFields optional return fields * @return {object} a standard result object */ self.createItem = function (parentId, name,mime, optFields) { if(!parentId || typeof parentId !== "string") { throw 'parentId invalid for create item'; } return self.urlPost (self.apiBase() + self.joinParams(self.fields(optFields),true) , { title:name, parents:[{id:parentId}], mimeType:mime }); } Get files by name, or create one if it doesn't existThis will find a file of the given name, or create it if it doesn't exist.
var result = dapi.getFilesByNameOrCreate library code /** * get files by name or create * @param {string} parentId the parentId * @param {string} name the name * @param {string} optMime the mime type * @param {string} optFields the fields to return */ self.getFilesByNameOrCreate = function (parentId, name , optMime, optFields) { var result = self.getChildItems (parentId, optMime, optFields , "title='" + name + "'"); if (result.success && result.data && !result.data.items.length) { // lets create it. var r = self.createItem(parentId , name , optMime, "id"); // double check to make sure it got created result = self.getChildItems (parentId, optMime, optFields , "title='" + name + "'"); } return result; }; Recursively get all files in a folder and its sub folders that match a particular queryThis will return the data of all script files owned by me under a particular folder and its sub folders
var result = dapi.getRecursiveChildItems (parentId , dapi.getEnums().MIMES.SCRIPT , "items(id)", "'me' in owners") library code /** * get child items for all folders and sub folders * @param {string} parentId the id of the parent * @param {ENUM.MIMES} optMime the mime type * @param {Array.string} optExtraQueries * @param {object} standard result */ self.getRecursiveChildItems = function (parentId,mime,optFields,optExtraQueries) { var r = recurse (parentId, []); // hack result a bit to return consolidated items r.result.data.items = r.items; return r.result; // recursive get contents of all the directories + scripts function recurse(id, items) { // get any scripts here var result = self.getChildItems (id, mime,"items(id)",optExtraQueries); // accumulate script files if(result.success) { cUseful.arrayAppend(items, result.data.items); // now recurse any folders in this folder result = self.getChildFolders (id,"items(id)"); if (result.success) { result.data.items.forEach(function(d) { recurse (d.id , items); }); } } return {items:items , result:result} ; } } Get the data in a fileUp till now we've been getting metadata from files. Eventually you'll want to actually get (and write) some data. Here's how.
var result = dapi.getContentById library code /** * get content of a file given its id * @param {string} id the id * @return {object} a standard result object */ self.getContentById = function (id) { return self.urlGet(self.apiBase() + "/" + id + "?alt=media"); }; Update the data in a fileYou should first create or find the file using one of the methods above, then update it using its id.
var result = dapi.getContentById library code /** * put content of a file given its id * @param {string} id the id * @param {string || object} content the content * @return {object} a standard result object */ self.putContentById = function (id,content) { return self.urlPost (self.apiBase("upload") + "/" + id + "?uploadType=media",content,"PUT"); }; You'll find the other 'behind the scenes code' in the full source belowFor more on drive SDK see Using Drive SDKYou want to learn Google Apps Script?Learning Apps Script, (and transitioning from VBA) are covered comprehensively in my my book, Going Gas - from VBA to Apps script, All formats are available from O'Reilly, Amazon and all good bookshops. You can also read a preview on O'Reilly If you prefer Video style learning I also have two courses available. also published by O'Reilly.
|
Services > Desktop Liberation - the definitive resource for Google Apps Script and Microsoft Office automation > Drive SDK and GitHub >