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
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
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
I'm trying to get attachment from Gmail to Google Drive. After create a new file into the folder, I have encountered a problem accessing the new file that script created.
same as the makeCopy Function
err: Exception: The document is inaccessible. Please try again later. at myFunction(Code:21:37)
Appreciated any help and suggestions. Thank you!
function myFunction() {
const files = DriveApp.getFolderById("xxxxxxxxxxxxxxxxxxx").getFiles();
while (files.hasNext()){
var file = files.next();
var filename = file.getName();
const aname = "xxxx.docx"
if (filename == aname){
var oldid = file.getId();
var copyfile = file.makeCopy().setName("COPYYYYY");
var newid = copyfile.getId();
}
}
var body = DocumentApp.openById(newid).getBody();
Logger.log(oldid);
Logger.log(newid);
Logger.log(body);
}
Modification points:
In your script, I think that when file of var copyfile = file.makeCopy().setName("COPYYYYY"); is Google Document, the script works. But from your error message and const aname = "xxxx.docx", I thought that you might try to have directly opened the DOCX file using DocumentApp.openById. If it's so, the reason of your issue is this.
In order to open the DOCX file using DocumentApp.openById, in the current stage, it is required to convert the DOCX file to Google Document.
When above points are reflected to your script, it becomes as follows.
Modified script:
Before you use this script, please enable Drive API at Advanced Google services.
function myFunction() {
const files = DriveApp.getFolderById("xxxxxxxxxxxxxxxxxxx").getFiles();
var newid = "";
while (files.hasNext()){
var file = files.next();
var filename = file.getName();
const aname = "xxxx.docx";
if (filename == aname){
var oldid = file.getId();
if (file.getMimeType() == MimeType.MICROSOFT_WORD) {
var copyfile = Drive.Files.copy({title: "COPYYYYY"}, oldid, {convert: true});
newid = copyfile.id;
}
}
}
if (newid) {
var body = DocumentApp.openById(newid).getBody();
Logger.log(oldid);
Logger.log(newid);
Logger.log(body);
}
}
In this modified script, the DOCX file is converted by the method of "Files: copy" of Drive API.
Reference:
Files: copy
I would like to save gmail attachments(.pdf files) from a specific email in a specific Google Drive folder. I also need to rename file with a string composed by some string of the email.
I have developed a simple script using Google Apps Script with some functions.
This is the main function I have wrote:
function GmailToDrive() {
var query = '';
query = 'in:inbox from:noreply#agyo.io has:nouserlabels ';
var threads = GmailApp.search(query);
var label = getGmailLabel_(labelName);
var parentFolder;
if (threads.length > 0) {
parentFolder = getFolder_(folderName);
}
var root = DriveApp.getRootFolder();
for (var i in threads) {
var mesgs = threads[i].getMessages();
for (var j in mesgs) {
//get attachments
var attachments = mesgs[j].getAttachments();
var message_body = mesgs[j].getBody();
for (var k in attachments) {
var attachment = attachments[k];
var isDefinedType = checkIfDefinedType_(attachment);
if (!isDefinedType) continue;
var attachmentBlob = attachment.copyBlob();
var file = DriveApp.createFile(attachmentBlob);
file.setName(renameFile_(attachment, message_body))
parentFolder.addFile(file);
root.removeFile(file);
}
}
threads[i].addLabel(label);
}
}
The checkIfDefinedType_(attachment) function checks if the attachments is a .pdf file and the renameFile_(attachment, message_body) rename the attachment extracting some string from the email.
The script seems to be correctly developed but sometimes I have two or more same attachments saved in my google drive folder.
Stefano, I had the same issue, if this is the same code as adapted from here.
I removed the line for (var i in fileTypesToExtract) { which was causing duplicates for me. It was running the query for each of the file types.
// `has:pdf` searches for messages with PDF attachments
var query = 'has:pdf in:inbox from:noreply#agyo.io has:nouserlabels ';
var results = Gmail.Users.Messages.list(userId, {q: query});
results.messages.forEach(function (m) {
var msg = GmailApp.getMessageById(m.id);
msg.getAttachments().forEach(function (a) {
var fileName = a.getName();
fileName = saveAttachmentToFolder(folder, a, fileName, msg.getDate(), input.tz);
});
});
function saveAttachmentToFolder(folder, attachment, fileName, date, timezone) {
if (timezone) {
fileName = standardizeName(attachment, date, timezone);
}
Logger.log(fileName);
folder.createFile(attachment.copyBlob()).setName(fileName);
}
The code snippet above is based on a Gmail add-on that I created, specifically for saving attachments to labeled folders in Drive: https://github.com/ellaqezi/archiveByLabel/blob/main/Code.gs#L24
In the label field, you can define nested directories to create in Drive e.g. foo/bar.
In the query field, you can copy the parameters as you would use them in Gmail's search bar.