I have a Google spreadsheet file with almost 2000 rows of URL such as this one:
https://docs.google.com/uc?id=0B4ptELk-D3USblk1aWd1OWowRWs
Each URL contains an image, stored in Google Drive. My question is: How can I transform this link ( https://docs.google.com/uc?id=0B4ptELk-D3USblk1aWd1OWowRWs) to a file name? For instance, I would like to know if it could be a /something/folder/Photofile1.jpg.
Is there anyway of doing this?
Try script:
function TESTgetFileName() {
Logger.log(getFileName('0B4ptELk-D3USVi1NZ3ZGbkhqYXc'));
// ^^^^^^^^^^ file ID ^^^^^^^^^
}
function getFileName(id) {
var file = DriveApp.getFileById(id);
var fileName = file.getName();
var strFolders = getFolders(file);
return strFolders + '/' + fileName;
}
function getFolders(object) {
var folders = object.getParents();
if (!folders.hasNext()) { return 'My Drive'; }
var folder = folders.next();
var folderNames = [];
while (folder.getParents().hasNext()) {
var folderName = folder.getName();
folderNames.unshift(folderName);
folder = folder.getParents().next();
}
return folderNames.join('/');
}
Script function will return the result:
0B4ptELk-D3USVi1NZ3ZGbkhqYXc → My Drive/something/folder/Photofile1.jpg
Tests
I opened script editor, selected function TESTgetFileName and get the result
My Drive/Detalhes_Farmacia.Fotografia_Fachada_1.JPEG.
This function can be run from script, not directly from spreadsheets. When you try using it as custom formula from sheet, it throws error:
You do not have permission to call getFileById (line 8).
So better use it from script to get data and then write the result to the sheet.
Related
This is my app script code that converts google docs in a folder into pdf format. The script stops after converting around 60 documents with maximum execution time error. I am converting around hundreds of files in a run. What can I do to avoid this error?
//Module to convert doc to pdf
function gdocToPDF() {
var documentRootfolder = DriveApp.getFolderById("xx") // replace this with the ID of the folder that contains the documents you want to convert
var pdfFolder = DriveApp.getFolderById("xx"); // replace this with the ID of the folder that the PDFs should be put in.
var documentRootFiles = documentRootfolder.getFiles()
while(documentRootFiles.hasNext()) {
createPDF(documentRootFiles.next().getId(), pdfFolder.getId(), function (fileID, folderID) {
if (fileID) createPDFfile(fileID, folderID);
})
}
}
function createPDF(fileID, folderID, callback) {
var templateFile = DriveApp.getFileById(fileID);
var templateName = templateFile.getName();
var existingPDFs = DriveApp.getFolderById(folderID).getFiles();
//in case no files exist
if (!existingPDFs.hasNext()) {
return callback(fileID, folderID);
}
for (; existingPDFs.hasNext();) {
var existingPDFfile = existingPDFs.next();
var existingPDFfileName = existingPDFfile.getName();
if (existingPDFfileName == templateName + ".pdf") {
Logger.log("PDF exists already. No PDF created")
return callback();
}
if (!existingPDFs.hasNext()) {
Logger.log("PDF is created")
return callback(fileID, folderID)
}
}
}
function createPDFfile(fileID, folderID) {
var templateFile = DriveApp.getFileById(fileID);
var folder = DriveApp.getFolderById(folderID);
var theBlob = templateFile.getBlob().getAs('application/pdf');
var newPDFFile = folder.createFile(theBlob);
var fileName = templateFile.getName().replace(".", ""); //otherwise filename will be shortened after full stop
newPDFFile.setName(fileName + ".pdf");
}
I generate a lot of files. What I do to avoid this error is open a dialog and run a function that creates one file at a time. I write the number of remaining files in a cell in the sheet, update it with every run. I use the success and failure handlers to decide whether to continue with file generation or close the dialog. You can generate files for hours in this way.
I was wondering: is it even possible to use Logger.Log in Google Apps Script to log different string to be posted to a spreadsheet?
I have the following code:
var ss = SpreadsheetApp.openByUrl("spreadsheet url");
var sheet = ss.getSheetByName("spreadsheet sheet");
var DocNumber = e.parameter.DocNumber;
var folderId = "Folder ID 1";
var lastFileUrl = getLatestFile(folderId); // just a function that retrieves url of latest file in the folder
Logger.log(lastFileUrl);
var addUrl = sheet.getRange(1,2,sheet.getLastRow(),1);
var fileURL = "https://drive.google.com/uc?export=view&id="+lastFileUrl;
var folderId2 = "Folder ID 2";
var lastFileUrl2 = getLatestFile(folderId2); // same as above
Logger.log(lastFileUrl2);
var addUrl2 = sheet.getRange(1,3,sheet.getLastRow(),1);
var fileURL2 = "https://drive.google.com/uc?export=view&id="+lastFileUrl2;
sheet.appendRow([DocNumber,fileURL,fileURL2]);
}
When this get posted to the spreadsheet, it only posts the second url (fileURL2) - I assume because the last value in the log is this. But I was hoping to post both URL into the spreadsheet.
I tried setting it as a var first as well:
var URL2 = Logger.log(lastFileURL2);
but then the posted value will be https://drive.google.com/uc?export=view&id=Logger
I also tried using appendRow before the second URL logging but it still only takes the second url and disregard the first url.
Therefore, I was curios whether this is even possible at all?
And if not, what's the best way to achieve this without using Logger.log?
Spreadsheet output:
URL1 and URL2 is the URL from Google Drive folder.
Also, forgot to mention, I'm using the script as a Web App, used by an android app. Posting files into the Drive folder is okay, the only problem is fetching the links of the files in different folders.
These are the codes I used to get the latest file url from my folders:
function getLatestFile(folderId) {
var files = DriveApp.getFolderById("Folder_1_ID").getFiles();
var fileObj = [];
while (files.hasNext()) {
var file = files.next();
fileObj.push({id: file.getId(), date: file.getDateCreated()});
}
fileObj.sort(function(a, b) {return new Date(b.date) - new Date(a.date)});
return fileObj[0].id;
}
function getLatestFile(folderId2) {
var files2 = DriveApp.getFolderById("Folder_2_ID").getFiles();
var fileObj2 = [];
while (files2.hasNext()) {
var file2 = files2.next();
fileObj2.push({id: file2.getId(), date: file2.getDateCreated()});
}
fileObj2.sort(function(a, b) {return new Date(b.date) - new Date(a.date)});
return fileObj2[0].id;
}
Problem
Having two functions declared under the same name
Solution
Step by step:
Remove one of the functions (they are identical in terms in usage)
Make the remaining one use the parameter passed in it:
function getLatestFile(folderId) {
var files = DriveApp.getFolderById(folderId).getFiles();
var fileObj = [];
while (files.hasNext()) {
var file = files.next();
fileObj.push({id: file.getId(), date: file.getDateCreated()});
}
fileObj.sort(function(a, b) {return new Date(b.date) - new Date(a.date)});
return fileObj[0].id;
}
Change Logger to console - as of recently, all logs are sent to Stackdriver service, and thus there is no benefit in using Logger (besides by using console you make script more portable).
Commentary
What happens when you declare two or more functions under same name? Normally, the last one declared gets executed (basically, second declaration overwrites the first):
function clone(original) {
return `I am the clone of ${original}`;
}
function clone(cloned) {
return `I am a clone of ${cloned}'s clone`;
}
const elem = document.querySelector("#cloned");
elem.textContent = clone("Gary");
<h2 id="cloned"></h2>
Many thanks for the comments and response. That code was a little too advanced for me and I ended up finding a very inelegant solution that I used because I ran out of time. I was able to get the code to list Folder and first level of subFolders with links, but I have not yet been able to get it to iterate through all levels of folders, mostly because I just need to back up and learn a lot of the basics. I was also able to get all folders to list using some code I found to create a tree, but I couldn't get it to format in a way that you could actually see the structure, or add links. I'm going to continue to try, and will post if I sort it out. Here is what I used, which was fine for our purposes because our shared drive is fairly limited.
For reference, this was the code I used to start with:
https://superuser.com/questions/1095578/list-of-subfolder-names-and-file-links-in-google-sheets-script
function listFolders(foldername) {
var ss = SpreadsheetApp.openById(ID);
var sheet = ss.getSheetByName("sheet name");
sheet.appendRow("Parent Folder", "Name", "Link" ]);
//change the folder ID below to reflect your folder's ID (look in the
URL when you're in your folder)
var folders = DriveApp.getFolderById(ID);
var contents = folders.getFolders();
var cnt = 0;
var folderD;
while (contents.hasNext()) {
var folderD = contents.next();
cnt++;
data = [
folders.getName(),
folderD.getName(),
folderD.getUrl(),
];
sheet.appendRow(data);
};
};
Original Post:
I am a beginner using script in google sheets and I am trying to create a list of folders in a google drive with many subfolders. Ideally it would be a tree form but I'd settle for a list at this point. I don't need to list all the files, just the folders. I have been trying to get the code below to work but it keeps hanging up at calling up the spreadsheet. Can anyone help?
I have tried calling up both the folders and the spreadsheet by both name and ID but it always tells me it can't execute the getactivespreadsheet command. I have also tried to modify the code referred to in another another question but I can't get that to work either: https://ctrlq.org/code/19923-google-drive-files-list
function generateFolderIndex(myfoldername) {
var folder = DriveApp.getFolderById('0B8vOJQUb-IIVTHdudlZSVkdtdE0');
var subFolders = folder.getFolders();
var childFolders = subFolders
var ss = SpreadsheetApp.getActiveSpreadsheet('1Trv9OtJFnD4AdSHrZKFfsSu6JMV9f78H6wwZNhF2_M4');
var sheet = ss.getSheetByName('Directory');
sheet.clear(directory);
sheet.appendRow([name, link]);
while (subFolders.hasNext())
{
var childFolder = childFolders.next();
var foldername = childFolder.getname();
var name = childFolder.getName()
var link = childFolder.getUrl()
var date = childFolder.getDateCreated()
data = [name, link]
sheet.appendRow(data);
}
};
I am trying to get a sheet that lists folders and subfolders with URL links. I am currently receiving the following error message:
[19-05-31 15:32:20:911 EDT] Execution failed: Cannot find method getActiveSpreadsheet(string). (line 5, file "Code") [0.432 seconds total runtime]
Or.. the easy way...
Use DRIVE or FS DRIVE APP for desktop in PC. Usea A CMD (windows)... AND THE FUNCTION
TREE >a.txt
The generated file a.txt will display all the tree.
IT SAVES HOURS OF RESEARCH.
SpreadsheetApp.getActiveSpreadsheet() doesn't have any parameters.
However
SpreadsheetApp.openById('ssid') does require and id. I think perhaps you meant to be using openById();
openById
getActiveSpreadsheet
This is a script that I'm currently working on but it generates a list of Spreadsheets and you can exclude folders by id and files by id.
function getAllSpreadsheets() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('FilesAndFolders');
if(sh.getLastRow()>0) {
sh.getRange(1,1,sh.getLastRow(),2).clear().clearDataValidations();
}
getFnF();
SpreadsheetApp.getUi().alert('Process Complete')
}
var level=0;
function getFnF(folder) {
var folder= folder || DriveApp.getRootFolder();
//var folder=DriveApp.getRootFolder();
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('FilesAndFolders');
var files=folder.getFilesByType(MimeType.GOOGLE_SHEETS)
while(files.hasNext()) {
var file=files.next();
if(isExcluded(file.getId(),'file')){continue;}
var firg=sh.getRange(sh.getLastRow() + 1,level + 1);
firg.setValue(Utilities.formatString('=HYPERLINK("%s","%s")',file.getUrl(),'FILE: ' + file.getName()));
firg.offset(0,1).insertCheckboxes('Exclude','Include');
}
var subfolders=folder.getFolders()
while(subfolders.hasNext()) {
var subfolder=subfolders.next();
if(isExcluded(subfolder.getId(),'folder')){continue;}
var forg=sh.getRange(sh.getLastRow() + 1,level + 1);
forg.setValue(Utilities.formatString('=HYPERLINK("%s","%s")',subfolder.getUrl(),'FOLDER: ' + subfolder.getName()));
//forg.offset(0,1).insertCheckboxes('Exclude','Include');
//level++;
getFnF(subfolder);
}
//level--;
}
function isExcluded(id,type) {//type: file or folder
var type=type||'Missing Input';
var xFldrIdA=['Excluded folder ids'];
var xFileIdA=['Excluded file ids'];
var type=type.toLowerCase();
switch(type) {
case 'file':
return xFileIdA.indexOf(id)>-1;
break;
case 'folder':
return xFldrIdA.indexOf(id)>-1;
break;
default:
throw(Utilities.formatString('Error: Invalid Type: %s in isExcluded.',type));
return true;//assume excluded
break;
}
}
Your welcome to use it, perhaps it will help.
I am currently using this code to automatically convert all uploaded .xls files in Google Drive to Google Sheets.
function importXLS(){
var files = DriveApp.searchFiles('title contains ".xls"');
while(files.hasNext()){
var xFile = files.next();
var name = xFile.getName();
if (name.indexOf('.xls')>-1){
var ID = xFile.getId();
var xBlob = xFile.getBlob();
var newFile = { title : name,
key : ID,
'parents':[{"id":"12FcKokB-ppW7rSBtAIG96uoBOJtTlNDT"}]
}
file = Drive.Files.insert(newFile, xBlob, {
convert: true
});
}
}
}
It works perfectly, but fails if there is already a file in the output folder with the same name. Even though I never technically get to see this error below (since it runs on a schedule and not fired manually like in the screenshot), I would prefer to simply skip the conversion process if the file already exists.
If possible, I would also like to avoid overwriting it each time, as I feel that would be a waste of processing time. How would I edit this code to say that if the file name already exists in that folder, skip the entire code completely?
Thanks!
Two things you can try:
Get the files names that are already in the destination folder and check if the file exists before you try copying.
Wrap the section of your code that does the copying in a try..catch statement.
Both of these should work independently, but using the try..catch statement will catch all errors, so it would be best to combine them. (You can review the error logs in the Developer Console.) Doing this you'll be able to skip files that have the same name as those already in your destination folder and any other error that might come up will not terminate your script from completing.
function importXLS(){
var files = DriveApp.searchFiles('title contains ".xls"');
var destinationFolderId = "12FcKokB-ppW7rSBtAIG96uoBOJtTlNDT";
var existingFileNames = getFilesInFolder(destinationFolderId);
while(files.hasNext()){
var xFile = files.next();
var name = xFile.getName();
try {
if (!existingFileNames[name] && (name.indexOf('.xls')>-1)) {
var ID = xFile.getId();
var xBlob = xFile.getBlob();
var newFile = { title : name,
key : ID,
'parents':[{"id": destinationFolderId}]
}
file = Drive.Files.insert(newFile, xBlob, {
convert: true
});
}
} catch (error) {
console.error("Error with file " + name + ": " + error);
}
}
}
/**
* Get an object of all file names in the specified folder.
* #param {string} folderId
* #returns {Object} files - {filename: true}
*/
function getFilesInFolder(folderId) {
var folder = DriveApp.getFolderById(folderId);
var filesIterator = folder.getFiles();
var files = {};
while (filesIterator.hasNext()) {
var file = filesIterator.next();
files[file.getName()] = true;
}
return files;
}
I am attempting to run the sample script included in the Scripts connected app. The code is as follows:
function listFilesInFolder(id) {
var folder = DriveApp.getFolderById(id);
var contents = folder.getFiles();
var file;
var name;
while(contents.hasNext()) {
file = contents.next();
name = file.getName();
Logger.log(name);
}
};
I have two questions about this:
Why am I receiving an error stating: Access denied: DriveApp. (line 10, file "Code")?
How do I pass in parameters for id when trying to run the script?
Also, an authorization window popped up to request access to drive by the script, which I allowed.
You can call this function from another one, or you can define 'id' inside of it. Example:
function testFilesInFolder() {
listFilesInFolder('folderIdHere');
}
or can define 'id' inside there:
function listFilesInFolder(id) {
id = 'folderIdHere'
var folder = DriveApp.getFolderById(id);
var contents = folder.getFiles();
var file;
var name;
while(contents.hasNext()) {
file = contents.next();
name = file.getName();
Logger.log(name);
}
};