Skip to content

Instantly share code, notes, and snippets.

@freetimecoder
Forked from brokensandals/GoogleDocsBackup.gs
Last active November 6, 2022 09:53
Show Gist options
  • Save freetimecoder/dc916ace62b0e67f89ed80e617b6f630 to your computer and use it in GitHub Desktop.
Save freetimecoder/dc916ace62b0e67f89ed80e617b6f630 to your computer and use it in GitHub Desktop.
Google Docs/Sheets/Slides automated backups script

Fork that modifies the script to only export office types and tries to increase speed. Potentially not as stable as the original!

Original Readme

Update: I now use a slightly different version of this script, which creates a single zip file instead of one per document, and puts a timestamp in the filename rather than overwriting the previous backup file. That version can be found at https://github.com/brokensandals/export-google-docs.

Google Apps Script that exports all your Google Docs/Sheets/Slides into docx/xlsx/pptx files and PDFs into a folder in your Google Drive. For more info and step-by-step setup instructions, see here: http://brokensandals.net/google-docs-backup

Replace INSERT_FOLDER_ID_HERE with the ID of the folder you want backups to be placed in.

Create a trigger to run the backupAll function if you want to do this on a schedule (e.g. nightly).

Notes:

  • By default, only files that you own (as opposed to files others have shared with you) will be backed up. Remove the file.getOwner() check from the backupAll method if you want to change that.
  • For each file, both an Office file (docx/xlsx/pptx) and a PDF are generated, and combined into a zip file that's placed in the backup folder. Zipping the backup files ensures that they don't clutter up the recent activity list for Docs/Sheets/Slides.
  • The script depends on the lastUpdated dates being correct on both the input files and the files in the backup directory. If that seems problematic, you could change the createOrUpdateFileForBlob method to delete existing backup files rather than updating them.

As always, this code may have defects that prevent it from working properly. Use at your own risk and remember to periodically verify that your backups are actually working as expected.

var BACKUP_FOLDER_ID = 'INSERT_FOLDER_ID_HERE';
var NATIVE_MIME_TYPES = {};
NATIVE_MIME_TYPES[MimeType.GOOGLE_DOCS] = MimeType.MICROSOFT_WORD;
NATIVE_MIME_TYPES[MimeType.GOOGLE_SHEETS] = MimeType.MICROSOFT_EXCEL;
NATIVE_MIME_TYPES[MimeType.GOOGLE_SLIDES] = MimeType.MICROSOFT_POWERPOINT;
var NATIVE_EXTENSIONS = {};
NATIVE_EXTENSIONS[MimeType.GOOGLE_DOCS] = 'docx';
NATIVE_EXTENSIONS[MimeType.GOOGLE_SHEETS] = 'xlsx';
NATIVE_EXTENSIONS[MimeType.GOOGLE_SLIDES] = 'pptx';
//Really these urls should be queried via exportLinks, but it's faster to hard code
var NATIVE_EXPORT_URLS = {};
NATIVE_EXPORT_URLS[MimeType.GOOGLE_DOCS] = "https://docs.google.com/feeds/download/documents/export/Export?id=";
NATIVE_EXPORT_URLS[MimeType.GOOGLE_SHEETS] = "https://docs.google.com/spreadsheets/export?id=";
NATIVE_EXPORT_URLS[MimeType.GOOGLE_SLIDES] = "https://docs.google.com/feeds/download/presentations/Export?id=";
//Only backup owned files, not shared files
var ONLY_OWNED = true;
var BACKUP_MIME_TYPES = Object.keys(NATIVE_MIME_TYPES);
function backupAll() {
const backupFolder = DriveApp.getFolderById(BACKUP_FOLDER_ID);
BACKUP_MIME_TYPES.forEach(function(mimeType) {
//Iterate through all files on the drive
var files = DriveApp.getFilesByType(mimeType);
while (files.hasNext()) {
var file = files.next();
//If owner check is enabled, skip files that are not owned
if(ONLY_OWNED && file.getOwner()) {
if (file.getOwner().getEmail() != Session.getActiveUser().getEmail()) {
continue;
}
}
//Check if older backups exist of this file
var existingFiles = backupFolder.getFilesByName(file.getName()+"."+NATIVE_EXTENSIONS[mimeType])
if(existingFiles.hasNext()) {
var existingFile = existingFiles.next();
//Don't backup if existing backup is up to date
if(file.getLastUpdated() < existingFile.getLastUpdated())
{
continue;
}
}
//Backup file
backup(file, backupFolder);
}
});
}
function backup(file, folder) {
try {
var native = getNativeBlob(file);
createOrUpdateFileForBlob(native, folder);
} catch (e) {
// Logs an ERROR message.
console.error('backup() yielded an error: ' + e);
}
}
function createOrUpdateFileForBlob(blob, folder) {
var existingFiles = folder.getFilesByName(blob.getName());
if (existingFiles.hasNext()) {
var file = existingFiles.next();
updateFile(file, blob);
} else {
var tmp = Utilities.newBlob([], blob.getContentType(), blob.getName());
// folder.createFile can only handle blobs up to 10MB, so we'll create
// an empty file first and then upload to it.
// We could use the upload API to create and upload the file at the same
// time, but that's a bit more work.
var file = folder.createFile(tmp);
updateFile(file, blob);
}
}
//Upload file over existing file
function updateFile(file, blob) {
const url = 'https://www.googleapis.com/upload/drive/v2/files/' + file.getId() + '?uploadType=media';
const params = {
method: 'put',
headers: { Authorization: 'Bearer ' + ScriptApp.getOAuthToken() },
payload: blob
};
var response = UrlFetchApp.fetch(url, params);
if (response.getResponseCode() < 200 || response.getResponseCode() > 299) {
throw 'Failed to update file named ' + file.getName();
}
}
function getNativeBlob(file) {
const nativeMimeType = NATIVE_MIME_TYPES[file.getMimeType()];
const extension = NATIVE_EXTENSIONS[file.getMimeType()];
const exportURL = NATIVE_EXPORT_URLS[file.getMimeType()];
//Construct query url
const url = exportURL + file.getId() + '&exportFormat='+extension;
const params = {
method: 'get',
headers: { Authorization: 'Bearer ' + ScriptApp.getOAuthToken() }
};
//Download converted file
const blob = UrlFetchApp.fetch(url, params).getBlob();
var fileName = file.getName() +"."+ extension;
//Set filename
blob.setName(fileName);
return blob;
}
@BuletRush
Copy link

Great code, thanks a lot!
I have a lot of folders (directories). And this script backs all the docs up into one single file and ignores the directories. Could you please help to adjust the script so it keeps all the folders and structure?
Also, I'm trying to get rid of the pdf duplicates..
Thanks in advance!

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