Google Apps Script, create a csv file and keep appending - google-apps-script

I'd like to create a csv file by appending to the file, rather than create file in one go. Is that possible?
DriveApp.createFile uses one go creation. Can there be problems where I read a huge number of records and would rather grab so many records and append to file? Is there a limit to the size of array I create in google apps script ? I am thinking of going over half a million records in csv file. Thanks.

Google Apps Script does not have an "append" method using Drive API,
You can create a file and then update it's entire content like below:
function appendDataToCSV(content){
var fileId = '', // update this file with it's ID
csvFile = DriveApp.getFileById(fileId),
currentFileContent = "",
stringContent = "",
delimiter = ";";
//in case your content is a 2D array like:
// [[1,2,3,4],[5,6,7,8]]
if(content instanceof Array){
for(var i=0;i<content.length;i++){
stringContent += (content[i].join ? content[i] : content).join(delimiter);
}
}
stringContent = stringContent || content || "";
if(stringContent){
// get file's current text content
currentFileContent = csvFile.getBlob().getDataAsString();
// update the csv file with its previous content + the new one adding a new row between
csvFile.setContent(currentFileContent + "\n" + stringContent);
}
return "CSV File updated! New size: "+csvFile.getSize();
}
But, it has a 10MB max file size limitation;
Best regards!

Related

Prevent Auto-Format DriveApi 3 Google Apps script

Using the Drive API3, I'm looking for a way to make a copy of a CSV file in Google Sheets format, without having to convert the text to numbers, nor the functions and dates as it can be proposed in the Google Sheets menu:
File>Import>(Select your CSV file)> Untick "Convert text to number, dates and formula".
At the moment, I've got something such as :
function convert(){
var file = DriveApp.getFileById('1234');
var resource = { title : "Title", mimeType : MimeType.GOOGLE_SHEETS,parents : [{id: file.getParents().next().getId()}],}
Drive.Files.copy(resource,file.getId())
}
To illustrate my example : I've got a text in my CSV file "2021-25-03", if I run my macro, the new spreadsheet will automaticaly format my text to a Date and that's not my goal.
TFR.
There doesn't seem to be a setting in the API or in Apps Script to prevent the automatic conversion of numbers and dates, but we can build a script to work around this. Two tools are useful:
Apps Script's Utilities.parseCsv() method, which will build a 2D array of the values in the CSV file (as pure text--it does not interpret numbers and dates).
The fact that Google Sheets interprets any value starting with a single quote ' as text. This is true whether the value is entered in the UI or programmatically.
So the overall strategy is:
Copy the file as you are doing (or just create a new blank file, as we will write the values to it).
Parse the CSV values and prepend a ' to each one.
Write these modified values to the sheet.
Something like this:
function convert(){
var file = DriveApp.getFileById(CSV_FILE_ID);
// Create the copy:
var resource = { title : "Title", mimeType : MimeType.GOOGLE_SHEETS,parents : [{id: file.getParents().next().getId()}],}
var sheetsFile = Drive.Files.copy(resource,file.getId())
// Parse the original csv file:
var csv = Utilities.parseCsv(file.getBlob().getDataAsString())
// csv is a 2D array; prepend each value with a single quote:
csv.forEach(function(row){
row.forEach(function(value, i){
row[i] = "'" + value
})
})
// Open the first (and only) sheet in the file and overwrite the values with these modified ones:
var sheet = SpreadsheetApp.openById(sheetsFile.id).getSheets()[0]
sheet.getRange(1,1,csv.length, csv[0].length).setValues(csv)
}

Google Apps Script - get URL of File in Drive with File Name

I am attempting to create a form in Google Spreadsheets which will pull an image file from my Drive based on the name of the file and insert it into a cell. I've read that you can't currently do this directly through Google Scripts, so I'm using setFormula() adn the =IMAGE() function in the target cell to insert the image. However, I need the URL of the image in order to do this. I need to use the name of the file to get the URL, since the form concatenates a unique numerical ID into a string to use the standardized naming convention for these files. My issue is that, when I use getFilesByName, it returns a File Iteration, and I need a File in order to use getUrl(). Below is an snippet of my code which currently returns the error "Cannot find function getUrl in object FileIterator."
var poNumber = entryFormSheet.getRange(2, 2);
var proofHorizontal = drive.getFilesByName('PO ' + poNumber + ' Proof Horizontal.png').getUrl();
packingInstructionsSheet.getRange(7, 1).setFormula('IMAGE(' + proofHorizontal + ')');
If you know the file name exactly, You can use DriveApp to search the file and getUrl()
function getFile(name) {
var files = DriveApp.getFilesByName(name);
while (files.hasNext()) {
var file = files.next();
//Logs all the files with the given name
Logger.log('Name:'+file.getName()+'\nUrl'+ file.getUrl());
}
}
If you don't know the name exactly, You can use DriveApp.searchFiles() method.
You're close - once you have the FileIterator, you need to advance it to obtain a File, i.e. call FileIterator.next().
If multiple files can have the same name, the file you want may not be the first one. I recommend checking this in your script, just in case:
var searchName = "PO + .....";
var results = DriveApp.getFilesByName(searchName);
var result = "No matching files";
while (results.hasNext()) {
var file = results.next();
if (file.getMimeType() == MimeType. /* pick your image type here */ ) {
result = "=IMAGE( .... " + file.getUrl() + ")");
if (results.hasNext()) console.warn("Multiple files found for search '%s'", searchName);
break;
}
}
sheet.getRange( ... ).setFormula(result);
You can view the available MimeTypes in documentation

Overwrite an Image File with Google Apps Script

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()})
}

How to make Flex AIR auto generate an Integer in a text box with the coordination of number of JSON files?

What I wanted to happen,
When a form pops-up, the text box would automatically generate an Integer,
regarding of the total number of JSON files located in my documentsdirectory +1.
The text box would be the file name of my JSON file and be saved in documentsdirectory.
After clicking save, form closes and save.
Repeats step 1.
You can count the number of JSON files using something like the function below that iterates through a directory and looks for files with extension "json".
private function countJsonFiles(dir:File):int
{
var numJsonFiles = 0;
//iterate through directory and make entries for assets
for each(var file:File in dir.getDirectoryListing())
{
if (!file.isDirectory)
{
//the last condition assumes you only want to count files with extension "json"
//I'm not sure if you want to count all the files or a different extension,
//so you can modify that part as needed
if (file.name != "." && file.name != ".." && file.extension == "json")
{
numJsonFiles++;
}
}
return numJsonFiles;
}
}
Then you can write to a new JSON file with its name concatenated with the number of JSON files so far plus 1:
var file:File = File.documentsDirectory.resolvePath("jsonFile" + countJsonFiles(File.documentsDirectory) + 1 + ".json");
var stream:FileStream = new FileStream();
stream.open(file, FileMode.WRITE);
stream.writeUTFBytes(text_box.text);
stream.close();
A more elegant solution to this would be to use an AIR SQLite database. You can find all the info you need here: http://help.adobe.com/en_US/as3/dev/WS5b3ccc516d4fbf351e63e3d118676a5497-7fb4.html
Basically you can create a simple table that has just one column "numJSONFiles" and increment this whenever a new file is created. That way, you always remember what the last index was that you used for a json file.
I don't recommend using EncryptedLocalStore here as it's not really sensitive information and doesn't quite fit the criteria found here: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/data/EncryptedLocalStore.html
Hope this helps!

Google apps script to delete old files permanently and keep last 100 files

I created a folder in my root google Drive that contains video files (.avi). I need to write a google apps script to delete the old video files permanently when the total numbers of the files are more than 100 files? i.e deleting all video files except last (newer) 100 files.
The name for each file is related to the time that this file were created example: 2013-02-25__20-29-45-01.avi
2013-02-25__20-24-49-09.avi
2013-02-25__18-26-24-08.avi
......
So I think the script should first list these files alphabetical starting with the newer and ended with the old one, then keep first 100 files and delete all others permanently.
I know how to do that in bash script, but not in google drive which I think they use javascript (.gs).
As I said in the comments, the script you referred to was not very far from what you want... but I admit your situation is a bit more complex so let's say this will be another exception to sto politics ;-)
That said, I didn't test this code thoroughly so it will probably need some tuning. I left a couple of commented logs throughout the script to test intermediate results, don't hesitate to use them. Also, think about updating the mail adress and don't forget that setTrashed can be manually reversed ;-) (better so when trying new code)
EDIT : I took some time this morning to test the script, it had a couple of "approximations";-)
here is a "clean" version that works nicely
function DeleteMyOldAvi() {
var pageSize = 200;
var files = null;
var token = null;
var i = null;
var totalFiles = []
var toDelete = []
Logger.clear()
do {
var result = DocsList.getAllFilesForPaging(pageSize, token);
var files = result.getFiles()
var token = result.getToken();
for(n=0;n<files.length;++n){
if(files[n].getName().toLowerCase().match('.avi')=='.avi'){
totalFiles.push([files[n].getName(),files[n].getDateCreated().getTime(),files[n].getId()]);// store name, Date created in mSec, ID in a subarray
// Logger.log(files[n].getName()+' created on '+Utilities.formatDate(files[n].getDateCreated(), 'GMT','MMM-dd-yyyy'))
}
}
} while (files.length == pageSize);// continue until job is done
totalFiles.sort(function(x,y){ // sort array on milliseconds date created (numeric/descending)
var xp = x[1];
var yp = y[1];
return yp-xp ;
});
// Logger.log(totalFiles.length)
if(totalFiles.length>100){
for(nn=totalFiles.length-1;nn>=100;nn--){
toDelete.push(totalFiles[nn]) ;// store the files to delete
}
// Logger.log(toDelete)
for(n=0;n<toDelete.length;++n){
var file = toDelete[n]
DocsList.getFileById(file[2]).setTrashed(true);// move to trash each file that is in the toDelete array
Logger.log(file[0]+' was deleted');// log the file name to create mail message
}
MailApp.sendEmail('myMail#gmail.com', 'Script AUTODELETE report', Logger.getLog());// send yourself a mail
}else{
MailApp.sendEmail('myMail#gmail.com', 'Script AUTODELETE report', 'No file deleted');// send yourself a mail
}
}