I have managed to record a folder's file/folder directory into a Google Sheet. From this Google Sheet I would like to copy this folder structure by reading each row.
Google Sheet Link: https://docs.google.com/spreadsheets/d/1qgDd8PEmHSYz5IsN9banYBjbmAyoLLVe3WMnOvmHdlE/edit?usp=sharing
function copyFolderTree() {
const sourceFolderId = '1uTGq2MRHzbev23sQzzFCi3Pl-v-ntMql';
const sourceFolder = DriveApp.getFolderById(sourceFolderId).getName();
const ss = SpreadsheetApp.getActiveSpreadsheet();
const ws = ss.getSheetByName('Sheet1');
const data = ws.getDataRange().getValues();
range.shift()
const destinationRootFolder = DriveApp.createFolder(`${sourceFolder}`)
data.forEach(function(row){
Logger.log(row)
let depth = row[4]
if (row[4] === depth && row[2] === 'Folder') {
Logger.log(`Name of folder is currently ${row[4]}`)
Logger.log(`Depth is currently ${row[4]}`)
Logger.log(`Type is currently ${row[2]}`)
destinationRootFolder.createFolder(row[0])
row.push('hello')
Logger.log(row)
range.setValues(values);
}
})
}
I realise this is the incorrect thinking. Do I need to place the newly created folder into it's copied parent folder using the folder's name?
Thank you,
[1]: https://docs.google.com/spreadsheets/d/1qgDd8PEmHSYz5IsN9banYBjbmAyoLLVe3WMnOvmHdlE/edit?usp=sharing
For folder tree, you can try
function copyFolderTree() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const ws = ss.getSheetByName('Sheet1');
const range = ws.getDataRange();
const values= range.getValues();
var levels = []
values.forEach(r => {
if (r[2]=='Folder'){
if (r[4]==0){ // root
var dossier = DriveApp.createFolder(r[0]);
levels[1] = dossier.getId(); // specific correction as 1 doesn't exists in the example provided
}
else {
var parent = DriveApp.getFolderById(levels[r[4]-1]);
var dossier = parent.createFolder(r[0]);
levels[r[4]] = dossier.getId();
}
}
})
}
From From this Google Sheet I would like to copy this folder structure by reading each row. and First I would like to create the folder structure. My next step would be to work on transferring the files across., when you want to copy a folder (in your sample Spreadsheet, it's "Amy Bits".) including all subfolders and files using Google Apps Script, how about the following sample script? I have created a Google Apps Script library for achieving this situation. So in this answer, I would like to propose the script using the Google Apps Script library.
Usage:
1. Install library.
You can see the method for installing the library at here.
2. Enable Drive API.
In this library, Drive API is used. So please enable Drive API at Advanced Google services.
3. Sample script:
When your sample Spreadsheet is used, the sample script is as follows.
function myFunction() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
const sourceFolderId = sheet.getRange("B2").getValue().split("/")[5]; // Please set the source folder ID.
const destinationFolderId = DriveApp.createFolder("sample").getId(); // Please set the destination folder ID.
const object = { sourceFolderId, destinationFolderId, overwrite: true };
const res = CopyFolder.copyAllFilesFolders(object);
console.log(res);
}
If you can directly put the source folder ID and the destination folder ID, you can also the following sample script.
function myFunction() {
const object = {
sourceFolderId: "###", // Please set the source folder ID.
destinationFolderId: "###", // Please set the destination folder ID.
overwrite: true,
};
const res = CopyFolder.copyAllFilesFolders(object);
console.log(res);
}
Note:
If the script process time is over 6 minutes, you can see one more sample script. Ref
Reference:
CopyFolder of Google Apps Script library
Related
I've made the script in google sheets and code of gs script (code.gs) it has been working fine and nice with the download function it is downloading each and every image in my google drive folder. But the main thing is that I want them to be downloaded with renaming them in the mean time with the column of text values available on the adjacent column
I just want the images to be renamed in the mean time when they are being downloaded in the google drive folder.
for example FIRST COLUMN ==>> "PICTURE URL" SECOND COLUMN ==>> "TEXT_TO_BE_RENAMED"
I've made the download button, when I click the download button it should execute the command to download the image and as well as rename the image the the values available on second column. Example of it is shown in below screen shot. I'm also sharing the sheet link below:
[HERE IS THE PICUTRE DEMONSTRATION WHAT I I WANT]
https://docs.google.com/spreadsheets/d/1jItuI2tQbpH4A5b9CFS9xWV3mNtnrV2jMRYPxEZSvlM/edit?usp=sharing
My code:
function downloadImage() {
let sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
let lastRow = sheet.getLastRow();
for (let i = 0; i < lastRow - 1; i++) {
let folder = DriveApp.getFolderById("Folder_id_of_your_drive");
let url = sheet.getRange(2 + i, 1).getValue();
let image = SpreadsheetApp.newCellImage().setSourceUrl(url);
let blob = UrlFetchApp.fetch(url).getBlob();
folder.createFile(blob);
}
}
I literally tried every thing to produce the result but unfortunately failed I am expecting the result that the images can be downloaded with the renamed text available in the separate list of cells values or with the adjacent columns
You can check for errors and rename the files like this:
function downloadImage() {
const folder = DriveApp.getFolderById('...folder ID goes here...');
const sheet = SpreadsheetApp.getActiveSheet();
const urls = sheet.getRange('A2:A').getDisplayValues().flat();
const names = sheet.getRange('C2:C').getDisplayValues().flat();
urls.forEach((url, index) => {
if (!url) return;
try {
const blob = UrlFetchApp.fetch(url).getBlob();
folder.createFile(blob).setName(names[index]);
} catch (error) {
console.log(error.message);
}
});
}
For additional information, see Folder.createFile() and the Blob class.
function downloadandrename() {
const f = DriveApp.getFolderById('fid');
const sh = SpreadsheetApp.getActiveSheet();
const vs = sh.getRange('A2:C' + sh.getLastRow()).getDisplayValues();
vs.forEach((r, i) => {
if (r[0] && r[2]) {
f.createFile(UrlFetchApp.fetch(r[0]).getBlob().setName(r[2]));
}
});
}
I have to keep the last modified files(any files) and remove(move to trash) other files having similar name from any folder or subfolder within google drive using app script only. How can I do it? please share code if possible. Thanks!
Delete All Sheets from All subfolders
function keepLastUpdated() {
const fldr = DriveApp.getRootFolder();
const ss = SpreadsheetApp.getActive();
ss.toast('Entry');
findLastUpdated(fldr);
ss.toast('EOF');
}
//var level = 1;
function findLastUpdated(folder = DriveApp.getRootFolder()) {
const ss = SpreadsheetApp.getActive();
let fs = [];
const files = folder.getFilesByType(MimeType.GOOGlE_SHEETS);// or MICROSOFT_EXCEL you choose
while (files.hasNext()) {
let file = files.next();
fs.push(file)
}
fs.sort((a,b) => {
return new Date(b.getLastUpdated()).valueOf() - new Date(a.getLastUpdated()).valueOf();
});
fs.shift();
fs.forEach(f => Drive.Files.remove(f.getId()));
const subfolders = folder.getFolders()
while (subfolders.hasNext()) {
let subfolder = subfolders.next();
//level++;
findLastUpdated(subfolder);
}
//level--;
}
This could take a while. It takes me about six to eight minutes to build a tree file structure of my drive in a spreadsheet and I only have about 3.06 GB used in my drive. I use the levels for keeping track of my recursion depth. I believe it can go to a max of 1000
This is a question that has been asked before but I'm struggling to adapt the answers to my needs.
references:
How to export to CSV from spreadsheet to drive or download folder
https://gist.github.com/mrkrndvs/a2c8ff518b16e9188338cb809e06ccf1
Initiate a download from google apps script
The structure seems to be:
create function in code.gs
create download.html
My workflow:
I have a bunch of files (20+) which I edit both manually and using a GAS.
To start with I've created a folder on Google Drive which I upload my CSV files to. I then run a standalone GAS to add a sum formula in column F to each file:
function addFormula() {
const folder = DriveApp.getFolderById('folderID');
const list = [];
const files = folder.getFiles();
while (files.hasNext()){
file = files.next();
let ssnameid = [];
ssnameid.push(file.getName(),file.getId());
list.push(ssnameid);
}
for (let g=0,len=list.length;g<len;g++) {
let id = list[g][1]; // list of Spreadsheet names[0] and ID numbers[1]
let csv = DriveApp.getFileById(id);
let docName = csv.getName();
let ss = SpreadsheetApp.openById(id);
let sheet = ss.getSheetByName(docName+'.csv');
let contents = sheet.getRange('A1:K100').getValues().filter(e => e[0]);
let rows = contents.length;
console.log(docName+' - number of rows: '+rows);
let cellF = 'F'+(rows+1);
let formulaF = '=SUM($F$2:$F$'+rows+')';
sheet.getRange(cellF).setValue(formulaF);
}
Then I go through each file, check if there are any other edits I need to make, and download as a CSV (File > Download > Comma Separated Values (.csv)). I was hoping to save time by also writing a function to download all the files as CSV.
So after making any manual edits, I then want to run a function in a standalone GAS to download all the files in the Google Drive folder as CSV files.
The answers I've found generally involve adding menu items and having pop-ups, and I don't know enough to make them suitable for a standalone GAS - I don't want any menu items or pop-ups, I just want to run a function which downloads a CSV.
For instance, how would I adapt this answer from Dr-Bracket?
Or this answer from soMarios, which works but only saves it to another folder in Google Drive, rather than downloading.
The reason I feel that having an HTML file work with a GS is that I've created a standalone function with this structure to send out emails. Using an HTML email template, I created a function in a standalone GAS to send out emails.
Is this the right approach for batch downloading files as CSV?
Thank you
Further references/clues:
https://developers.google.com/apps-script/guides/html/templates#code.gs https://developers.google.com/apps-script/guides/html/reference/run#index.html https://developers.google.com/apps-script/reference/drive/file#getDownloadUrl()
EDIT - My Solution
The workaround is to send all the files to a folder on Google Drive and then download the folder. So the benefit is only downloading one folder rather than downloading each file. Here's the code adapted from the soMarios answer linked to above:
function saveCSV() {
/** sourceFolder contains all the Google Sheets you want to save as CSV files */
const sourceFolder = DriveApp.getFolderById('folderID');
const list = [];
const files = sourceFolder.getFiles();
while (files.hasNext()){
file = files.next();
let ssnameid = [];
ssnameid.push(file.getName(),file.getId());
list.push(ssnameid);
}
console.log(list);
for (let g=0,len=list.length;g<len;g++) {
let id = list[g][1]; // list of Spreadsheet names[0] and ID numbers[1]
let csv = DriveApp.getFileById(id);
let docName = csv.getName();
let ss = SpreadsheetApp.openById(id);
let sheet = ss.getSheetByName(docName+'.csv');
/** save files as CSV to Google Drive folder */
let requestData = {"method": "GET", "headers":{"Authorization":"Bearer "+ScriptApp.getOAuthToken()}};
let sheetID = sheet.getSheetId().toString();
let url = "https://docs.google.com/spreadsheets/d/"+id+"/export?gid="+sheetID+"&format=csv"
let result = UrlFetchApp.fetch(url, requestData);
let resource = {
title: docName+'.csv',
mimeType: 'application/vnd.csv',
parents: [{ id: 'downloadFolderID' }]
}
Drive.Files.insert(resource,result)
}
}
Note that for this to work you need to add Drive API (Services > Add a Service > Drive API)
To download a sheet as csv whitout any further manipulation, try this auto-download script
gs
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('M E N U')
.addItem('auto download', 'autoDownload')
.addToUi();
}
function autoDownload() {
var html = HtmlService.createHtmlOutputFromFile('download');
SpreadsheetApp.getUi().showModalDialog(html, 'CSV download interface');
}
function saveAsCSV() {
var ssid = 'your spreadsheet id';
var folderID = 'temporary folder id'
var csv = "";
var ss = SpreadsheetApp.openById(ssid)
ss.getSheets()[0].getDataRange().getValues().forEach(function (r) {
csv += r.join(",") + "\n";
});
var url = DriveApp.getFolderById(folderID)
.createFile(ss.getName() + '.csv', csv, MimeType.CSV)
.getDownloadUrl()
.replace("?e=download&gd=true", "");
return url;
}
download.html
<!DOCTYPE html>
<html>
<body>
Auto Download CSV ... please wait
</body>
<script>
function executeDownload(url) {
window.location.href = url;
}
window.onload=function() {
google.script.run
.withSuccessHandler(executeDownload)
.saveAsCSV();
window.setTimeout(function(){google.script.host.close()},9000);
}
</script>
</html>
tested with chrome
you can modify ssid, temporary folder id and setTimeout parameter to optimize
I'm horrible at coding so I'm sure this will need some work.
My Problem: My company does work for a lot of other companies (example Drive hierarchy photo attached). Each company we work with gets their own folder that is nested under the folder 'Shirts for other companies". We put all their assets into these folders (logos, mocks, quotes).
We want to build a script that will look into the individual company folders and take any file that starts with the word Mock and automatically copy it and put it into the database folder found in the picture below.
Here is the script have so far. but I'm having trouble getting into the subfolders (ford tshirts and designs). Not only that, but if it runs everyday the script will keep duplicating the files it has duplicated in the past which I want to avoid.
Script:
Function getTheFiles() {
var dApp = DriveApp;
var folderIter = dApp.getFolderByName("Shirts for other companies");
var folder = folderIter.next();
var filesIter = folder.getFiles();
var dataBase = folder.getFoldersByName("database1").next();
var i = 1;
while(filesIter.hasNext()) {
var file = filesIter.next();
var filename = file.getName();
if(filename = "mocking") {
file.makeCopy(dataBase);
}
logger.log(filename);
i++;
}
If I understood you correctly, you want to:
Copy all files from the different subfolders inside a folder called "Shirts for other companies", that start with the word mock, to the Database folder (which is also inside the main folder).
Avoid files getting copied many times.
If that's the case, you could do the following:
Search for all files in each subfolder of the main folder that start with mock, excluding the files inside Database folder. For this, you iterate through all files in each subfolder (after checking the subfolder is not named Database folder. For each file, you check that the name start with mock, using the method substring.
To keep track of which files are copied, and so avoid making duplicates, you can use PropertiesService, which can store key-value pairs. The id of every copied file is copied to the script property, and then, the next time the script runs, it checks whether the id is in the property. If that's the case, the file doesn't get copied again. One thing to notice is that script properties can only store strings, so that arrays have to be converted to a string every time we want to store a new id (via toString, and back to an array, via split.
The function below does all these things (check inline comments for more details):
function copyFiles() {
var mainFolder = DriveApp.getFoldersByName("Shirts for other companies").next();
var folders = mainFolder.getFolders(); // Get folder iterator from main folder
var databaseFolderName = "Database folder"; // You database folder name
var databaseFolder = mainFolder.getFoldersByName(databaseFolderName).next(); // Get databse folder
var idsArray = []; // Array to store the ids of the copied files
while (folders.hasNext()) { // Iterate through each folder in the main folder
var folder = folders.next();
if (folder.getName() !== databaseFolderName) { // Check current folder is not the database folder
var files = folder.getFiles();
while (files.hasNext()) { // Iterate through each file in each subfolder
var file = files.next();
if (file.getName().substring(0, 4).toLowerCase() === "mock") { // Check that file name starts with "mock"
// Get ids of the files that were copied in previous executions:
var key = "copied-ids";
var scriptProperties = PropertiesService.getScriptProperties();
var ids = scriptProperties.getProperty(key);
if (ids) idsArray = ids.split(",");
else ids = "";
// Get current file id:
var id = file.getId();
// Check that current file id is not in properties (not copied before):
if (idsArray.indexOf(id) === -1) {
file.makeCopy(databaseFolder); // Copy file to database folder
idsArray.push(id); // Add file id to the array of copied files
}
ids = idsArray.toString();
scriptProperties.setProperty(key, ids);
}
}
}
}
}
Reference:
PropertiesService
String.prototype.substring()
Array.prototype.toString()
String.prototype.split()
I hope this is of any help.
This function would search your entire Google Drive for files starting the the letter Mock and put Name, url, id, type (folder or file) into the active spreadsheet and tab named MoclList;
function getAllMocks() {
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('MocksList');
sh1.clearContents();
sh1.appendRow(['Name','Url','Id','Type']);
getFnF();
SpreadsheetApp.getUi().alert('Process Complete')
}
var level=0;
function getFnF(folder) {
var folder= folder || DriveApp.getRootFolder();
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('MocksList');
var files=folder.getFiles();
while(files.hasNext()) {
var file=files.next();
if(file.getName().toLowerCase().slice(0,4)=='mock') {
var firg=sh.getRange(sh.getLastRow() + 1,level + 1);
firg.setValue(file.getName());
firg.offset(0,1).setValue(Utilities.formatString('=HYPERLINK("%s","%s")',file.getUrl(),'FILE: ' + file.getName()));
firg.offset(0,2).setValue(file.getId());
firg.offset(0,3).setValue('File');
}
}
var subfolders=folder.getFolders()
while(subfolders.hasNext()) {
var subfolder=subfolders.next();
var forg=sh.getRange(sh.getLastRow() + 1,level + 1);
forg.setValue(subfolder.getName());
forg.offset(0,1).setValue(Utilities.formatString('=HYPERLINK("%s","%s")',subfolder.getUrl(),'FOLDER: ' + subfolder.getName()));
forg.offset(0,2).setValue(subfolder.getId());
forg.offsert(0,3).setValue('Folder');
//level++;
getFnF(subfolder);
}
//level--;
}
I have a large number of PDF files split across multiple folders (in a tree structure) in my Google Drive. I'd like to retain the PDF files while also creating a copy of each PDF document in Google Docs format which has been OCRd. The Google Docs file needs to have the same name as the PDF file.
How do I do this?
As part of this, I tried to at least convert one file into PDF by code but ran into issues there as well.
function pdfToDoc() {
var fileBlob = DriveApp.getFileById('<ID>').getBlob();
var resource = {
title: fileBlob.getName(),
mimeType: fileBlob.getContentType()
};
var options = {
ocr: true
};
var docFile = Drive.Files.insert(resource, fileBlob, options); // <-- Google said "Empty response (line 10, file "Code")"
Logger.log(docFile.alternateLink);
}
I followed this tutorial but made some changes because I'm using v3 of Drive API. Here is the snippet:
var blob = DriveApp.getFileById('FILE_ID').getBlob();
Logger.log(blob)
var text = pdfToText(blob, {ocrLanguage: "en"});
Logger.log(text);
/**
* Convert pdf file (blob) to a text file on Drive, using built-in OCR.
* By default, the text file will be placed in the root folder, with the same
* name as source pdf (but extension 'txt'). Options:
*/
function pdfToText ( pdfFile, options ) {
// Ensure Advanced Drive Service is enabled
try {
Drive.Files.list();
}
catch (e) {
throw new Error( "Enable 'Drive API' in Resources - Advanced Google Services." );
}
// Prepare resource object for file creation
var parents = [];
var pdfName = "Sample Docs";
Logger.log(pdfName)
var resource = {
name: pdfName,
mimeType: MimeType.GOOGLE_DOCS,
parents: parents
};
// Save PDF as GDOC
resource.title = pdfName.replace(/pdf$/, '');
var insertOpts = {
'ocr': true,
'ocrLanguage': 'en'
}
Logger.log(resource.title)
var gdocFile = Drive.Files.create(resource, pdfFile, insertOpts);
// Get text from GDOC
var gdocDoc = DocumentApp.openById(gdocFile.id);
var text = gdocDoc.getBody().getText();
// Save text file, if requested
resource.name = pdfName.replace(/pdf$/, 'txt');
resource.mimeType = MimeType.PLAIN_TEXT;
var textBlob = Utilities.newBlob(text, MimeType.PLAIN_TEXT, resource.name);
var textFile = Drive.Files.create(resource, textBlob);
return text;
}
Initially, DriveApp cannot convert pdf to Google Docs directly so I used Advance Drive Service. Just follow this link on how to enable advanced services.
Hope this helps.