Hello I have a column with a list of Google Drive folder links and I would like to get latest updated date of all the files contained in the folders.
Example
By looking around I was able to build the following scripts which works fine by giving me the list of all the files contained in the folder and their latest update.
function onOpen() {
var spreadsheet = SpreadsheetApp.getActive();
var menuItems = [
{name: 'UpdateCheck', functionName: 'files'},
];
spreadsheet.addMenu('Menu', menuItems);
}
function files() {
var ui = SpreadsheetApp.getUi();
var result = ui.prompt("Paste the ID of Google Drive Folder");
var folderId = result.getResponseText();
var folder = DriveApp.getFolderById(folderId);
var files = folder.getFiles();
var output = [];
while (files.hasNext()) {
var file = files.next();
output.push([file.getName(), file.getLastUpdated()]);
}
output.sort(function(a, b) {
return a[0] == b[0] ? 0 : a[0] < b[0] ? -1 : 1;
});
SpreadsheetApp.getActiveSpreadsheet().insertSheet().getRange(2, 1, output.length, output[0].length).setValues(output);
However I have to use the UI to open each link, copy and paste the ID from the URL in the input field. I would like to just read the column range, get the IDs from the strings without duplicates and run my script and have my result in a new tab. Any help would be great.
I believe your current situation and your goal are as follows.
You have a sheet that the folder links are put to the column "D" in your providing image.
You want to retrieve the folder ID from the links and want to retrieve the filename and the value of getLastUpdated() of all files in the folders.
You want to put the retrieved data on a new sheet.
Modified script:
Please set the sheet names of srcSheetName and dstSheetName.
function myFunction() {
var srcSheetName = "Sheet1"; // Please set the source sheet name.
var dstSheetName = "Sheet2"; // Please set the destination sheet name.
// Retrieve folder links from "D2:D" and check the duplication.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(srcSheetName);
var folderIds = [...new Set(sheet.getRange("D2:D" + sheet.getLastRow()).getRichTextValues().flatMap(([d]) => {
var url = d.getLinkUrl();
if (url && /^https:\/\/drive\.google\.com\/drive\/folders\/.+/.test(url)) {
return [url.split("/")[5].trim()];
}
return [];
}))];
// Create an array for putting to sheet.
var res = folderIds.flatMap(id => {
var folder = DriveApp.getFolderById(id);
var files = folder.getFiles();
var temp = [];
while (files.hasNext()) {
var file = files.next();
temp.push([file.getName(), file.getLastUpdated()]);
}
return temp;
});
// Put the array to the destination sheet.
ss.insertSheet(dstSheetName).getRange(1, 1, res.length, 2).setValues(res);
}
About some unclear points, I guessed as follows.
I couldn't understand the format of folder links in the cells. In this modification, I guessed that the folder link is like https://drive.google.com/drive/folders/###.
I couldn't understand get the IDs from the strings without duplicates. In this modification, the duplication of retrieved folder IDs is checked.
References:
getValues()
insertSheet(sheetName)
setValues(values)
Added:
From the following your replying,
Quick question, when I run it I get an Unexpected error while getting the method or property getFolderById on object DriveApp at line var res = folderIds.flatMap(id => { var folder = DriveApp.getFolderById(id); it's solvable or should I open another thread?
the folder link was indeed incorrect instead of https://drive.google.com/drive/folders/### the format was https://drive.google.com/corp/drive/u/0/folders/####?resourcekey=#### so I correct the code with /^https://drive.google.com/corp/drive/u/0/folders/.+/.test(url)) { return [url.split("/")[8].trim()]; but now I get 1CHKEThZJ0P-ISv0g0MoGjCCqdZuuJJyA?resourcekey=0-1gzYTJvJv1XDwF2Hx4HvEQ and I need to find a way to get rid of everything right before the "?"
For this, I modified my proposed script as follows.
Modified script:
function myFunction() {
var srcSheetName = "Sheet1"; // Please set the source sheet name.
var dstSheetName = "Sheet2"; // Please set the destination sheet name.
// Retrieve folder links from "D2:D" and check the duplication.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(srcSheetName);
var folderIds = [...new Set(sheet.getRange("D2:D" + sheet.getLastRow()).getRichTextValues().flatMap(([d]) => {
var url = d.getLinkUrl();
if (url) {
if (/^https:\/\/drive\.google\.com\/drive\/folders\/.+/.test(url)) {
return [url.split("/")[5].trim()];
} else if (/^https:\/\/drive\.google\.com\/.+\/drive\/u\/0\/folders\/.+/.test(url)) {
return [url.split("/")[8].trim().split("?")[0]];
}
}
return [];
}))];
// Create an array for putting to sheet.
var res = folderIds.flatMap(id => {
try {
var folder = DriveApp.getFolderById(id);
var files = folder.getFiles();
var temp = [];
while (files.hasNext()) {
var file = files.next();
temp.push([file.getName(), file.getLastUpdated()]);
}
return temp;
} catch (e) {
console.log({id, msg: e.message});
}
});
// Put the array to the destination sheet.
ss.insertSheet(dstSheetName).getRange(1, 1, res.length, 2).setValues(res);
}
By this modification, 2 kinds of URLs like https://drive.google.com/drive/folders/### and https://drive.google.com/corp/drive/u/0/folders/####?resourcekey=#### can be used, and when an error occurs when the files are retrieved from the folder, you can see the folder ID at the log. And, the script is not stopped.
Related
I am using below script to pull date from all spreadsheets in a folder. It is strong and working.
now i would like to expand this script to define multiple different sheet names instead of one. that is because we have the same file in 3 different langueses, now when it is running and there is a file from an other langues it cannot find the sheet(name) so the script gives the error that GetRange is null. The data is the same, So Ill go through the files looking for
The sheets: "Gegevenskaart" || "DataCard" || "Datenkarte"
The Same for Range. in this specified Sheet I have 6 different ranges. they are all the same size and all have the same kind of data but there are rows that I dont want to use. (20 rows data, 25 empty rows in between)
The ranges to get data from are : [A15:BC34 ,A63:BC82 ,A11:BC130, A159:BC178 ,A207:BC226 ,A255:BC274, A300:3BC319]
(also these data rows have a string in colomn A while the other rows dont have a string)
function CombineDataToMasterFile() {
var folder = DriveApp.getFolderById("1234567890234567891234567");
var filesIterator = folder.getFiles();
var file;
var fileType;
var ssID;
var combinedData = [];
var data;
while(filesIterator.hasNext()){
file = filesIterator.next();
fileType = file.getMimeType();
if(fileType === "application/vnd.google-apps.spreadsheet"){
ssID = file.getId();
data = getDataFromSpreadsheet(ssID);
data = data.map(function(r){return r.concat([file.getName()]); });
combinedData = combinedData.concat(data);
}
}
var ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Datatest");
ws.getRange("A2:BC").clearContent();
ws.getRange(2, 1, combinedData.length, combinedData[0].length).setValues(combinedData);
}
function getDataFromSpreadsheet(ssID){
var ss = SpreadsheetApp.openById(ssID);
var ws = ss.getSheetByName("Blad1");
var data = ws.getRange("A15:BC34").getValues();
return data;
My script in my spreadsheet on Gdrive for insert link id of file in folders like this:
function listFolders(folder) {
var sheet = SpreadsheetApp.getActiveSheet();
sheet.appendRow(["Name", "FileId"]); //writes the headers
var folder = DriveApp.getFolderById("XXXXXXXXXX")
var subfolders = folder.getFolders();
while (subfolders.hasNext()) {
var name = subfolders.next();
var files = name.getFiles();
var cnt = 0;
var file;
while (files.hasNext()) {
var file = files.next();
cnt++;
data = [
file.getName(),
file.getId()
];
sheet.appendRow(data);
};
}
}
How to resolve this problem?
Thanks in advance.
I believe your goal as follows.
You want to retrieve the file list from the subfolders just under the specific folder using Google Apps Script.
You want to reduce the process cost of your script.
As the additional goal, you want to add "FolderName" to the column.
Modification points:
In your script, appendRow is used in a loop. In this case, the process cost becomes high. Ref In your situation, I thought that when the number of files is large, the process cost will become high. In this answer, this is modified.
It seems that folder of argument is not used.
When above points are reflected to your script, it becomes as follows.
Modified script:
function listFolders() {
var sheet = SpreadsheetApp.getActiveSheet();
var folder = DriveApp.getFolderById("XXXXXXXXXX")
var subfolders = folder.getFolders();
var values = [["Name", "FileId", "FolderName"]]; // Modified
while (subfolders.hasNext()) {
var name = subfolders.next();
var folderName = name.getName(); // Added
var files = name.getFiles();
while (files.hasNext()) {
var file = files.next();
data = [file.getName(), file.getId(), folderName]; // Modified
values.push(data);
}
}
var cnt = values.length;
console.log(cnt); // You can see the total number of files at the log.
sheet.getRange(sheet.getLastRow() + 1, 1, values.length, values[0].length).setValues(values);
}
In this modification, the file list is put to an array and the array including the file list is put to the Spreadsheet using setValues.
Note:
When you want to retrieve the file list from all subfolders under the subfolders in the specific folder, you can also use the following script.
function listFolders() {
const getAllFolders = (id, list = [["Name", "FileId", "FolderName"]]) => { // Modified
const fols = DriveApp.getFolderById(id).getFolders();
let temp = [];
while (fols.hasNext()) {
const fol = fols.next();
const folderName = fol.getName(); // Added
temp.push(fol.getId());
const files = fol.getFiles();
while (files.hasNext()) {
const file = files.next();
list.push([file.getName(), file.getId(), folderName]); // Modified
}
}
temp.forEach(id => getAllFolders(id, list));
return list;
}
const id = "###"; // Please set the top folder ID.
const values = getAllFolders(id);
var cnt = values.length;
console.log(cnt); // You can see the total number of files at the log.
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange(sheet.getLastRow() + 1, 1, values.length, values[0].length).setValues(values);
}
When above modification was not the direct solution of your issue, I would like to propose to use Drive API. In this case, I think that this Google Apps Script library can be also used. https://github.com/tanaikech/FilesApp
References:
Benchmark: Reading and Writing Spreadsheet using Google Apps Script
setValues(values)
Added:
About your following replying,
I want to know which folder on my drive this file belonged to. const getAllFolders = (folderName,id, list = [["FolderName","Name", "FileId"]]
Unfortunately, I'm not sure whether I could correctly understand about the output values of your additional question, the following script is what you want?
Sample script:
function sample() {
const getAllFolders = (id, list = [["FolderName", "Name", "FileId"]]) => {
const folder = DriveApp.getFolderById(id);
const folderName = folder.getName();
const fols = folder.getFolders();
let temp = [];
while (fols.hasNext()) {
const fol = fols.next();
temp.push(fol.getId());
const files = fol.getFiles();
while (files.hasNext()) {
const file = files.next();
list.push([folderName, file.getName(), file.getId()]);
}
}
temp.forEach(id => getAllFolders(id, list));
return list;
}
const id = "###"; // Please set the top folder ID.
const values = getAllFolders(id);
var cnt = values.length;
console.log(cnt); // You can see the total number of files at the log.
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange(sheet.getLastRow() + 1, 1, values.length, values[0].length).setValues(values);
}
This minor modification runs okay for me:
function listFolders() {
var sh = SpreadsheetApp.getActiveSheet();
sh.appendRow(["Name", "FileId","Count"]);
var folder = DriveApp.getFolderById("insert folder id")
var subfolders = folder.getFolders();
while (subfolders.hasNext()) {
var fldr = subfolders.next();
var files = fldr.getFiles();
var cnt = 0;
while (files.hasNext()) {
var file = files.next();
sh.appendRow([file.getName(),file.getId(),++cnt]);
};
}
}
This was my output:
Name
FileId
Count
Slide22.png
Redacted
1
Slide21.png
Redacted
2
Slide20.png
Redacted
3
Slide19.png
Redacted
4
Slide18.png
Redacted
5
Slide17.png
Redacted
6
Slide16.png
Redacted
7
Slide15.png
Redacted
8
Slide14.png
Redacted
9
Slide13.png
Redacted
10
var folder = DriveApp.getFolderById('1_gA4D7dfybJ60IdfgsnqdfdsgVoo9D76fgsdgf9cqmAnJI7g7');
var contents = folder.getFiles();
var file;
var name;
var sheet = SpreadsheetApp.getActiveSheet();
var date;
var size;
sheet.clear();
sheet.appendRow(["Nome", "Data", "Dimensione"]);
while(contents.hasNext()) {
file = contents.next();
name = file.getName();
date = file.getDateCreated();
size = file.getSize();
id = file.getUrl();
data = [name, date, size,id]
sheet.appendRow(data);
}
};
Every time all the data is cleared, after that the data is added from the starting.
sheet.clear();sheet.appendRow(["Nome", "Data", "Dimensione"]);
Is there any way to check the data?
if the same data present Already then the function does not work on the row.
If a new record is added, it should also be added, but the function does not start again from one.
If I have 20 PDFs in my folder, and add one more PDF in folder, then add the function 21 pdf and do not run the function from the beginning.
Explanation:
The logic behind the following script is the following:
We get all the URLs of the files that are currently in column D of the sheet. These are the URLs of the files that have been recorder so far. We can safely assume that the URLs are always unique:
const aURLs = sheet.getRange('D2:D'+sheet.getLastRow()).getValues().flat();
The second step is to iterate through the files as the original script would do and check if the URL of a file is in aURLs. If the file URL is not in aURLs or in other words in column D, then add it to the newFiles array:
if(!uNames.includes(name)){
newFiles.push(data);
}
After we checked all the files, we can add, after the last row of sheet, only the new files:
sheet.getRange(sheet.getLastRow()+1,1,newFiles.length,newFiles[0].length).setValues(newFiles);
Solution:
Manually add the headers in the file for the first time only:
and then execute the script every next time:
function myFunction() {
const folder = DriveApp.getFolderById('1_gA4D7dfybJ60IdfgsnqdfdsgVoo9D76fgsdgf9cqmAnJI7g7');
const contents = folder.getFiles();
const sheet = SpreadsheetApp.getActiveSheet();
const aURLs = sheet.getRange('D2:D'+sheet.getLastRow()).getValues().flat();
const newFiles = [];
while(contents.hasNext()) {
let file = contents.next();
let name = file.getName();
let date = file.getDateCreated();
let size = file.getSize();
let id = file.getUrl();
let data = [name, date, size,id]
if(!aURLs.includes(id)){
newFiles.push(data);
}
}
sheet.getRange(sheet.getLastRow()+1,1,newFiles.length,newFiles[0].length).setValues(newFiles);
}
I'm trying to get all text from a specific cell of all spreadsheets in a folder. My current issue is I can only read them in as a file type which doesn't allow me to access the getRange() function.
Here's my code so far.
function createLog() {
var folder = DriveApp.getFolderById("id#");//not placing actual id for privacy
var contents = folder.getFiles();
var data; //array for relevant text
var file;
var d = new Date();
var log = SpreadsheetApp.create("Events log "+d.getMonth()+"/"+d.getDay());
while(contents.hasNext()) {
file = contents.next();
file.getRange("A6");//error is here because it is a file type not a spreadsheet
}
for(var i =0; i<contents.length;i++){
log.getRange(0,i).setValue(data[i]);
}
}
Once you have the list of files you need to open them with SpreadsheetApp. Then you can work on Spreadsheet using the Sheet and Range functions.
var spreadsheet = SpreadsheetApp.openById(file.getId());
var sheet = spreadsheet.getSheetByName('your sheet name');
var value = sheet.getRange("A6");
See:
https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app
https://developers.google.com/apps-script/reference/drive/file#getId()
Cameron's answer is correct but I suggest to open spreadsheets by ID instead of names because in Google Drive many files can have the same name...
Below is a simplified demo code to test the idea and a few comments to highlight what needs to be adjusted
function createLog() {
var folder = DriveApp.getFolderById("0B3###############ZMDQ");//not placing actual id for privacy
var contents = folder.getFilesByType(MimeType.GOOGLE_SHEETS);
var data; //array for relevant text
var fileID,file,sheet;
var data = [];
var d = new Date();
var log = SpreadsheetApp.create("Events log "+d.getMonth()+"/"+d.getDay());
while(contents.hasNext()) {
file = contents.next();
Logger.log('Sheet Name = '+file.getName());
fileID = file.getId();
sheet = SpreadsheetApp.openById(fileID).getSheets()[0];// this will get the first sheet in the spreadsheet, adapt to your needs
data.push([sheet.getRange("A6").getValue()]);// add a condition before this line to get only the data you want
}
log.getActiveSheet().getRange(1,1,data.length, data[0].length).setValues(data);
}
I'm working on a project where I'm trying to get a sheet to autoupdate with info from a new sheet every day. The new sheet will be dropped in the same folder and will be given the same name every day. However, I need to get the new sheet key every day to make the code run with the new sheet importing data.
I'm almost done, now I just need to get the string that was pushed to variable key into the importrange function on line 37. The tricky part about this is that the code imports it into the target sheet as a string where it then executes the actual import range function when it hits the sheet.
I need a way to get the variable key into that string such that it will still execute in the sheet OR dump the id that the variable is holding somewhere and then put it into that string automatically. Thanks so much in advance!
var counter = 0;
var files = [];
var key = (" ");
function searchFolder() {
var folderId = '0B6wmHZ5c0fzfTjI1bFpKOHI3N3M'; // test folder
// Log the name of every file in the folder.
var filesN = DriveApp.getFolderById(folderId).getFiles(); //log files in folder
while (filesN.hasNext()) files.push(filesN.next().getName());
while (filesN.hasNext())
keyID.push(filesN.next().getId());
}
function autoUpdate(){ //updates monthly from newly imported daily
if (counter == 1){ //counter is made to be 1 when day is uploaded to monthly
var ss = SpreadsheetApp.openById("1lH9Y12P2Q2OFndIJoAU48ePggXFc9WGcWjolZMcABoc"); //defines target spreadsheet ie monthly
SpreadsheetApp.setActiveSpreadsheet(ss); //sets target spreadsheet as active
var range= ss.getRange("A1:A1"); //sets range in target. ONLY CHOOSE ONE
range.activate; // activates range
//HELP HERE PLEASE
range.setValue('=IMPORTRANGE("1hVv6ldHEaCCI_uptr0MpzAyP60x7on8YR_brWwWXTWo","sheet1!A1:167")'); //Puts in IMPORTRANGE into target as a STRING value (just words). Once it hits the sheet, then SHEETS executes IMPORTRANGE not SCRIPTS. In Source sheet, range is selected to import to target (ie A1:G6)
counter=(counter-1)
}
}
function timeStamp(){
if (files == "Daily") {
counter= (counter+1)
}
}
searchFolder();
timeStamp();
autoUpdate();
Did you click on the #REF? Because usually with an IMPORTRANGE function you have to "Allow Access" for it to pull data. Until you do this it will display a #REF. If you click on the cell a pop up should display with a button titled "Allow Access".
Once I did this on my test your original command, range.setValue('=IMPORTRANGE("1hVv6ldHEaCCI_uptr0MpzAyP60x7on8YR_brWwWXTWo","sheet1!A1:167")') worked.
Also to use a variable your command would look like this...
range.setValue(('=IMPORTRANGE("'+key+'","sheet1!A1:167")'))
Update Based on Comments
Change
var files = [];
var key = ("");
var folderId = '0B6wmHZ5c0fzfTjI1bFpKOHI3N3M'; // test folder
// Log the name of every file in the folder.
var filesN = DriveApp.getFolderById(folderId).getFiles(); //log files in folder
while (filesN.hasNext()) files.push(filesN.next().getName());
while (filesN.hasNext())
keyID.push(filesN.next().getId());
To...
var files = [];
var key = [];
var folderId = '0B6wmHZ5c0fzfTjI1bFpKOHI3N3M'; // test folder
// Log the name of every file in the folder.
var filesN = DriveApp.getFolderById(folderId).getFiles(); //log files in folder
while (filesN.hasNext()) {
var file = filesN.next();
key.push(file.getId())
files.push(file.getName());
}
Logger.log(key)
This will create an array of file keys as well as an array of name...you could then loop through the key array to get each key individually and use the key variable in the command as shown previously. I'm not really sure exactly what you are wanting to do so more specific help will need more information.
UPDATE 8-14-2016
This is what I meant regarding looping through the keys array. I've tested and it works.
function test() {
var files = [];
var keys = [];
var folderId = '0B6wmHZ5c0fzfTjI1bFpKOHI3N3M'; // test folder
// Log the name of every file in the folder.
var filesN = DriveApp.getFolderById(folderId).getFiles(); //log files in folder
while (filesN.hasNext()) {
var file = filesN.next();
keys.push(file.getId())
files.push(file.getName());
}
Logger.log(keys)
var ss = SpreadsheetApp.openById("1lH9Y12P2Q2OFndIJoAU48ePggXFc9WGcWjolZMcABoc"); //defines target spreadsheet ie monthly
SpreadsheetApp.setActiveSpreadsheet(ss); //sets target spreadsheet as active
var s = ss.getSheetByName('Sheet1');
for (var i = 0; i < keys.length; i++) {
var range = s.getRange(1,i+1); //sets range in target. ONLY CHOOSE ONE
range.setValue('=IMPORTRANGE("'+keys[i]+'","sheet1!A1:167")')
}
autoUpdate(keys);
}