My google apps script imports a csv file [test.csv] into a new tab, manipulates some values, then saves the manipulated tab as a csv file [test.csv]. When it saves the new version, it simply makes another copy [test(1).csv]. I wish instead to overwrite the previous file (or delete the old one then export/write the new version.) Help please?
I am using reference code from the Interacting With Docs List Tutorial
I know this is an old question, but much of the information in the accepted answer has been deprecated by Google since then. DocsList is gone, as are the clear() and append() methods on a file object.
I use the following function to create or overwrite a file:
// Creates or replaces an existing file
function updateFile (folder, filename, data) {
try {
// filename is unique, so we can get first element of iterator
var file = folder.getFilesByName(filename).next()
file.setContent(data)
} catch(e) {
folder.createFile(filename, data)
}
}
For reference, here's some code for doing the same for a folder. It assumes we're operating in the parent folder of the current sheet and want a folder
object for a new or existing folder there.
// Get folder under current folder. Create it if it does not exist
function getOrCreateFolder(csvFolderName) {
var thisFileId = SpreadsheetApp.getActive().getId();
var thisFile = DriveApp.getFileById(thisFileId);
var parentFolder = thisFile.getParents().next();
try {
// csvFolderName is unique, so we can get first element of iterator
var folder = parentFolder.getFoldersByName(csvFolderName).next();
// asking for the folder's name ensures we get an error if the
// folder doesn't exist. I've found I don't necessarily catch
// an error from getFoldersByName if csvFolderName doesn't exist.
fname = folder.getName()
} catch(e) {
var folder = parentFolder.createFolder(csvFolderName);
}
return folder
}
You could do DocsList.find(fileName) which gives you a list of files that have that name. If file names are unique, then you can just do var file = DocsList.find(fileName)[0].
If you are a Google Apps user, you can use file.clear() to remove all the contents of the old file, and then file.append() to insert all of the new contents.
Otherwise, you will have to file.setTrashed(true) and then DocsList.createFile() to make the new one.
Related
I want to execute a google apps script whenever a new file is added to a specific folder.
Currently I'm using a run-every-x-minutes clock trigger, but I only need to run the script whenever I add a file to a folder. Is there a way to do this?
The same as this question - which is now almost 3 years old. The comment below the question states that:
There's not a trigger for that, if that's what you're hoping. How are
things getting into the folder, and do you have any control over that?
– Jesse Scherer Apr 8 '18 at 3:02
I wonder if this comment is still valid, and if it is, then if there's a workaround.
Issue:
Unfortunately, the comment you read is still true. Here is a list of all the available triggers and a trigger for a new file added to a folder is not one of them.
Workaround / Explanation:
I can offer you a workaround which is usually used by developers when they built their add-ons. You can take advantage of the PropertiesService class. The logic is quite simple.
You will store key-value pairs scoped to the script:
In your case, the key will be the folder id, and the value will be the number of files under this folder.
You will setup a time-driven trigger to execute mainFunction for example every one minute.
The script will count the current number of files within the selected folder. The function responsible for that is countFiles.
The checkProperty function is responsible for checking if the current number of files under this folder matches the old number of files. If there is a match, meaning no files were added, then checkProperty returns false, otherwise return true and update the property for the current folder ID, so when the script runs after 1 minute, it will compare with the fresh value.
If checkProperty returns true, then execute the desired code.
Code snippet:
Set up a time-driven trigger for mainFunction. Whatever code you put inside the brackets of the if(runCode) statement will be executed if the number of files under the folderID has changed.
function mainFunction(){
const folderID = 'folderID'; //provide here the ID of the folder
const newCounter = countFiles(folderID);
const runCode = checkProperty(folderID, newCounter);
if(runCode){
// here execute your main code
//
console.log("I am executed!");
//
}
}
And here are the helper functions which need to be in the same project (you can put them in the same script or different scripts but in the same "script editor").
function countFiles(folderID) {
const theFolder = DriveApp.getFolderById(folderID);
const files = theFolder.getFiles();
let count = 0;
while (files.hasNext()) {
let file = files.next();
count++;
};
return count;
}
function checkProperty(folderID, newC){
const scriptProperties = PropertiesService.getScriptProperties();
const oldCounter = scriptProperties.getProperty(folderID);
const newCounter = newC.toString();
if(oldCounter){
if(oldCounter==newCounter){
return false;
}
else{
scriptProperties.setProperty(folderID, newCounter);
return true;
}
}
else{
scriptProperties.setProperty(folderID, newCounter);
return true;
}
}
so i created a simple google code that exports sheet to json, and I'm wondering if there's a simple way to:
Export the file to the same folder where the sheet that script running it is in,
Overwrite the file if it already exists (also in the same folder the sheet script is in)
cheers
There's no easy way, but here's how (I assume you're using Apps Script):
Get the file id of your spreadsheet https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#getId()
Get its parent folders - there can be more than one - https://developers.google.com/apps-script/reference/drive/file#getParents()
See if there are existing files with the name - there can be more than one - https://developers.google.com/apps-script/reference/drive/folder#getFilesByName(String)
If it exists set its content https://developers.google.com/apps-script/reference/drive/file#setcontentcontent
If it doesn't exist create it - https://developers.google.com/apps-script/reference/drive/folder#createfilename-content
thanx to Ian for research links, made it work :)
here's the snippet if anyone else is needing the same thing
var _ID = SpreadsheetApp.getActiveSpreadsheet().getId();
var _sheet = SpreadsheetApp.getActiveSpreadsheet();
var _file = DriveApp.getFolderById(_ID);
var _folder = _file.getParents().next();
var _parentFolder = _file.getParents().next();
var _existingJsons = _parentFolder.getFilesByName(sheet.getName()+".json");
// if .json with the same name already exists in this directory, just overwrite content
if (_existingJsons.hasNext()){
while (_existingJsons.hasNext() ){
var _json = _existingJsons.next();
_json.setContent(output);
Logger.log(_json.getName());
}
}
// if not, create new one, and move it in the same folder sheet is in
else {
var _json = DriveApp.createFile(sheet.getName()+".json", output, MimeType.PLAIN_TEXT);
_json.makeCopy(_parentFolder);
_json.setTrashed(true);
}
Starting with 'google apps script'. I've created a simple script that logs some information with 'Logger.log("message");' with this lines being in a loop, so the log message is quite large.
I'd like now to create an html file with that same information and either being able to download that file locally or have it downloaded to my google drive.
Any idea how that could be achieved? I've tried looking examples for a while but I cannot have anything working...
Many thanks!
Yes you can, you should read first. Have a look this documentation
function createMyHTMLlog()
{
var folders = DriveApp.getFoldersByName('ff');// ff:your folder name on drive
if (folders.hasNext())
{
var folder = folders.next(); //select the folder
folder.createFile('Log','<b> your message here </b>', MimeType.HTML); //create the file
}
}
Update: Add more content later
function logUpdate()
{
var children = folder.getFilesByName('Log');// log is the created file name
if (children.hasNext())
{
var file = children.next();
var logContent = file.getAs('text/plain').getDataAsString();// get available msg
logContent = logContent.concat('<br>your<b> updated message </b> here'); // combine with new msg
file.setContent(logContent);
}
}
Who knows how to merge folder with the same name in google drive using google script?
Step 01: Find if any duplicate folder in the root-Folder;
Step 02: If yes, choose one folder as main-folder, and then move all files in the other folder(with the same name) to this main-folder;
Step 03: Delete the other folder(with the same name).
Many thanks in advance.
Merge Folder with the same name under the same Folder
My approach:
Take parent folder in which you want to check for folders with same name
Loop over all subfolders and get their names
Find all folders within parent folder with each found folder name
Specify first iteration for given folderName as destination folder
If there is more than one folder with given name, move all files and folders from this folder to destination folder and trash that folder
Try this:
I have succesfully tested this function on some junk data.
function mergeDuplicateFolders() {
var parentFolder = DriveApp.getFolderById('{paste your folder ID here}'); //parent folder which will be scanned for duplicates
var childFolderIterator = parentFolder.getFolders(); //all folders within parent folder
//iteration over all folders in parent directory
while (childFolderIterator.hasNext()) {
var childFolder = childFolderIterator.next();
var childFolderName = childFolder.getName();
var childSameNameIterator = parentFolder.getFoldersByName(childFolderName);
//just check if folder with givn name exist (there should be at least one)
if(childSameNameIterator.hasNext()) {
var destFolder = childSameNameIterator.next();
//iteration over 2nd and all other folders with given name
while (childSameNameIterator.hasNext()) {
var toMergeFolder = childSameNameIterator.next();
var filesToMove = toMergeFolder.getFiles();
var foldersToMove = toMergeFolder.getFolders()
//iteration over all files
while (filesToMove.hasNext()) {
var file = filesToMove.next();
moveFile(file, destFolder);
}
//iteration over all subfolders
while (foldersToMove.hasNext()) {
var folder = foldersToMove.next();
moveFolder(folder, destFolder);
}
//trashes empty folder
toMergeFolder.setTrashed(true);
}
}
}
}
//custom function which removes all parents from speciefied file and adds file to new folder
function moveFile(file, destFolder) {
var currentParents = file.getParents();
while (currentParents.hasNext()) { // be careful, this loop will remove all parents from the file... if you want to have this file in multiple folders you should add if statement to remove it only from specified folder
var currentParent = currentParents.next();
currentParent.removeFile(file);
}
destFolder.addFile(file);
Logger.log("File moved to " + destFolder.getName());
}
//custom function which removes all parents from speciefied folder and adds that folder to new one
function moveFolder(folder, destFolder) {
var currentParents = folder.getParents();
while (currentParents.hasNext()) { // be careful, this loop will remove all parents from the folder... if you want to have this folder in multiple folders you should add if statement to remove it only from specified folder
var currentParent = currentParents.next();
currentParent.removeFolder(folder);
}
destFolder.addFolder(folder);
Logger.log("Folder moved to " + destFolder.getName());
}
Your application:
You can of course edit first two lines of this function and add parentFolderId as parameter of that function. Then you can use this function in your own iteration and check multiple folders for duplicates.
function mergeDuplicateFolders(parentFolderId) {
var parentFolder = DriveApp.getFolderById(parentFolderId);
//...continue with the code from above
Warnings
Be careful. In Google Drive you can have one file in multiple folders.
This code will remove files (in folders with same name) from all
parent folders and moves them only in one. If you prefere removing it
only from the folder with same name, you need to add some if statement
in moveFolder() and moveFile() functions.
Google script allows to run custom function only for (approx.) 6 minutes. If you have large amout of folders to check, you should make some workaround. You can add e.g. execution time limit. Example is here: Prevent Google Scripts from Exceeding the Maximum Execution Time Limit
EDIT:
Of course there is much simpler way to remove file only frome merged folder... Remove both custom functions and edit both iterations this way... I didn't realize it first.
//iteration over all files
while (filesToMove.hasNext()) {
var file = filesToMove.next();
toMergeFolder.removeFile(file);
destFolder.addFile(file);
}
//iteration over all subfolders
while (foldersToMove.hasNext()) {
var folder = foldersToMove.next();
toMergeFolder.removeFolder(folder);
destFolder.addFolder(folder);
}
Class Folder, currently, doesn't have existing method to move files from one folder in Google Drive to another.
And as far as I know, there is an existing issue on Seeing duplicate files after sync on desktop Drive version 1.14. However, you may follow the given workaround in the forum as follows:
Step 01:
How to Find duplicates files (by name and/or size) in a specific folder of my google drive
Step 02:
How do I copy & move a file to a folder in Google Apps Script?
Step 03: You can use removeFolder(child) method from Class Folder, which can only be used to remove a given folder from a current folder.
I hope that helps.
Can I overwrite an image file with Google Apps Script? I've tried:
file.setContent(newBlobImage);
file.replace(newBlobImage);
Neither of those work. .setContent() will delete whatever data was in the file, and it looks like maybe it just writes the variable name as text, or something like that. I'm assuming that both .setContent() and .replace() are meant for text documents, and maybe that's why they don't work.
If it were a text file, or a spreadsheet, I might be able to clear it, then append new content.
I can trash the file, then create a new one, but I'd rather not if there is some other way.
If I write a file with the same name, it won't overwrite the existing file, it creates a another file with the same name.
The only way I've been able to trash the file is with DocsList and the only success I've had with creating an image file is with DriveApp. So I have to trash the file with DocsList, then create another file with DriveApp.
Well, I've figured out how to delete the file without sending it to the trash, so I won't need to clean out the trash later. The Google Drive SDK inside of Apps Script has a remove method that didn't send the file to trash, it's just gone.
var myFolder = DriveApp.getFolderById('3Bg2dKau456ySkhNBWB98W5sSTM');
thisFile = myFolder.getFilesByName(myFileName);
while (thisFile.hasNext()) {
var eachFile = thisFile.next();
var idToDLET = eachFile.getId();
Logger.log('idToDLET: ' + idToDLET);
var rtrnFromDLET = Drive.Files.remove(idToDLET);
};
So, I'm combining the DriveApp service and the DriveAPI to delete the file without sending it to the trash. The DriveAPI .remove needs the file ID, but I don't have the file ID, so the file gets looked up by name, then the file ID is retrieved, then the ID is used to delete the file. So, if I can't find a way to overwrite the file, I can at least delete the old file without it going to the trash.
I just noticed that the DriveAPI service has a Patch and an Update option.
.patch(resource, fileId, optionalArgs)
Google Documentation Patch Updates file metadata.
The resource arg is probably the metadata. The fileId is self explanatory. I'm guessing that the optionalArgs are parameters that follow the HTTP Request Patch semantics? I don't know.
It looks like both Patch and Update will update data. Update is a PUT request that will
clears previously set data if you don't supply optional parameters.
According to the documentation. So it's safer to use a Patch request because any parameters that are missing are simply ignored. I haven't tried it yet, but maybe this is the answer.
I'm getting an error with Patch, so I'll try Update:
.update(resource, fileId, mediaData)
That has a arg for mediaData in the form of a blob. And I think that is what I need. But I'm not sure what the resource parameter needs. So I'm stuck there.
An image file can be overwritten with Google Apps Script and the DriveAPI using the update() method:
.update(File resource, String fileId, Blob mediaData)
Where file resource is:
var myFileName = 'fileName' + '.jpg';
var file = {
title: myFileName,
mimeType: 'image/jpeg'
};
I'm getting the file ID with the DriveApp service, and the Blob is what was uploaded by the user.
In order to use DriveAPI, you need to add it through the Resources, Advanced Google Services menu. Set the Drive API to ON.
var allFilesByName,file,myFolder,myVar,theFileID,thisFile;
//Define var names without assigning a value
file = {
title: myFileName,
mimeType: 'image/jpeg'
};
myFolder = DriveApp.getFolderById('Folder ID');
allFilesByName = myFolder.getFilesByName(myFileName);
while (allFilesByName.hasNext()) {
thisFile = allFilesByName.next();
theFileID = thisFile.getId();
//Logger.log('theFileID: ' + theFileID);
myVar = Drive.Files.update(file, theFileID, uploadedBlob);
};
Thank you for this track !
This allowed me to find a solution to my problem : move a bound form after copying and moved his spreadsheet.
The Drive app advanced service must be activated in the "Resource Script Editor" to run this script.
function spreadsheetCopy() {
// Below is the file to be copied with a bound form.
var fileToCopy = DriveApp.getFileById("file_key"); // key is fileId
var saveFolder = DriveApp.getFolderById("folder_key"); // key is folderId
var currentFolder = "";
( fileToCopy.getParents().next() ) ? currentFolder = fileToCopy.getParents().next() : currentFolder = DriveApp.getRootFolder();
Logger.log(currentFolder)
var copyFile = fileToCopy.makeCopy(saveFolder),
copyName = copyFile.getName();
Utilities.sleep(30000);
moveFormCopy(currentFolder, saveFolder, copyName);
}
function moveFormCopy(currentFolder, saveFolder, copyName) {
var formsInFolder = currentFolder.getFilesByType(MimeType.GOOGLE_FORMS);
var form, copyForm, copyFormMimeType, copyFormName, copyFormId;
while ( formsInFolder.hasNext() ) {
form = formsInFolder.next();
if ( copyName === form.getName() ) {
copyForm = form;
copyFormMimeType = copyForm.getMimeType();
copyFormName = copyForm.getName();
copyFormId = copyForm.getId();
break;
}
};
var resource = {title: copyName, mimeType: copyFormMimeType};
Drive.Files.patch(resource, copyFormId, {addParents: saveFolder.getId(), removeParents: currentFolder.getId()})
}