I'm trying to create script for creating contracts. For this will be in 'Template File which is going to be text file on gDrive' example:
Client's VAT: $VAT
Client's Name: $Clients_name
How do I get this file content ( getBody(), which works only on Document Types, but you are not able to access Document instance, because all possibilities I've found lead me to File instance.. )
My target is to get exact copy of 'Template File' and go through him and change words starting with dollar, changing them by values from HTTP request. ( this part is already done. )
EDIT:
you might be interested about my try ( or it can help someone )
function createContract(obj)
{
const templateName = "RC - Contract Template";
const templateFile = getTemplate(templateName);
const templateText = templateFile.getAs("plain/text");
const file = DocumentApp.create(obj.filename);
const body = file.getBody();
const url = file.getUrl();
body.insertParagraph(0, templateText);
HtmlService.createHtmlOutput(url);
}
Note: getTemplate() is function going through all files and returning instance of File with name 'RC - Contract Template'
After some searching I've found out, that google has a big difference between "File", "Document", etc etc.
If someone gets in same situation, this is my way to get Content of file, when you know only name:
function getTemplateId(filename) {
const files = DriveApp.getFilesByType(MimeType.GOOGLE_DOCS);
while (files.hasNext()) {
var file = files.next();
if (file.getName() == filename) return file.getId();
}
return HtmlService.createHtmlOutput("Required template: " + filename + " does not exists!");
}
Note:Moved Solution posted by OP from question:
Related
This is a continuation from How to Use Advanced Drive Service to Upload Files.
My Webapp consists of an upload form for data files, which are then stored on Google Drive. (Full code in snippet below.)
I'm having a problem with the following line of code:
var file = Drive.Files.insert(resource, mediaData); // create file using Drive API
try {
//Get root folder and pull all existing folders, plus setup variables pulled from form
var dropbox = form.Country;
var timeStamp = new Date();
//Set file name slightly differently for Weekly Member Report (do not want to overright based on name just keep each extract so add timestamp to name)
if (form.reportType == "Member Weekly"){
var filename = form.reportType + timeStamp + ".xls";
}
else
{
var filename = form.reportType+".xls";
}
var rootfolder = DriveApp.getFolderById("0Byvtwn42HsoxfnVoSjB2NWprYnRiQ2VWUDZEendNOWwwM1FOZk1EVnJOU3BxQXhwU0pDSE0");
//Note root folder is Live Uploads Folder in Flatworld App folder structure
var folder, folders = rootfolder.getFoldersByName(dropbox);
//Check if folder exists and if not create
if (folders.hasNext()) {
folder = folders.next();
} else {
folder = rootfolder.createFolder(dropbox);
}
var FolderURL = folder.getUrl(); // Retain URL of folder for final end message to user
//Check if file already exists and delete if it does
var file, files = folder.getFilesByName(filename);
while( files.hasNext()){
file = files.next();
file.setTrashed(true);
}
//New Code from Stackover Flow:
//Upload file and set various properties
var mediaData = form.myFile;
var resource = {
title: filename,
mimetype: 'application/vnd.ms-excel',
description: "Uploaded via BNI Upload Form by " + form.myName + " on: " + timeStamp
};
var file = Drive.Files.insert(resource, mediaData); // create file using Drive API
var fileId = file.id;
var DriveAppFile = DriveApp.getFileById(fileId); // retrieve file in DriveApp scope.
var FileURL = DriveAppFile.getUrl(); //Retain URL of file for final end message to user
DriveApp.removeFile(DriveAppFile); // remove new file from Users root My Drive
folder.addFile(DriveAppFile); // puts file in selected folder
//End of New code from Stackover Flow
//Success message displayed to user
return "Thanks! File uploaded successfully to: <br><br><b>Folder Location:</b> " + FolderURL + "<br>" + "<b>File:</b> " + FileURL + ". <br><br>For any queries please email user#example.com copying the URLs displayed here in your email. You can close this browser window now or use the link below to upload another file.<br><br>";
} catch (error) {
//Catch error return it to user and email with error details
Its throwing the error message "Empty Response" on the line of code above when we try and upload a large file (15MB) Do you have any suggestions. This is well inside the Files insert limit of 5120GB and the code works fine on smaller files.
I've now tried to add in a loop in function to try the upload a couple of times, still throwing the same error sadly:
//setup function that will return null if file is not uploaded correctly
function createDriveFile(resource_f, mediaData_f){
try{
var file = Drive.Files.insert(resource_f, mediaData_f); // create file using Drive API
return file;
} catch (e) {return null;}
}
//try upload and loop if null
var maxTries = 3;
var tries = 0;
do {
tries = tries + 1;
if (tries > 0) {
var file = createDriveFile(resource, mediaData);
Logger.log("I'm trying to upload, try number: " + tries);
}
} while ((file == null) && (tries < maxTries));
if (file == null) {
var file = Drive.Files.insert(resource, mediaData); // try one laste time to create file using Drive API - outside loop function so if error is thrown script stops
}
The error only seems to occur on a larger file, even if we reduce the size of the same file that solves error so do we need to adjust the upload process to account for a larger file. Is there a Google Apps Script equivalent of making the API upload request resumable?
Your file size is the determinant factor here. Referencing the documentation suggests the simple upload method used here is good for up to 5MB only.
https://developers.google.com/drive/web/manage-uploads
Your comments seem to confirm this is what is happening for you.
As you hinted, use the resumable upload method. Use the uploadType: resumable parameter flag – API docs on the insert method describes how.
You can also check the naming of the file, for me I had a slash in the name which is why it would not upload. So take away any special characters before uploading and it should work.
I am downloading a .zip from a website. It contains one .txt file. I would like to access the data in the txt and write it to a spreadsheet. I'm open to either accessing it directly and not extracting the zip OR extracting the zip, saving the txt to a Google Drive Folder, and accessing it once it is saved.
When I use Utilities.unzip(), I can never get it to unzip the file and usually end up with an "Invalid argument" error. In the code below, the last section before else contains the unzip command. It successfully saves the file to the correct Google Folder but then I can't extract it.
function myFunction() {
// define where to gather data from
var url = '<insert url here>';
var filename = "ReportUploadTesting05.zip";
var response = UrlFetchApp.fetch(url, {
// muteHttpExceptions: true,
// validateHttpsCertificates: false,
followRedirects: true // Default is true anyway.
});
// get spreadsheet for follow up info
var Sp = SpreadsheetApp.getActiveSpreadsheet();
if (response.getResponseCode() === 200) {
// get folder details of spreadsheet for saving future files
var folderURL = getParentFolder(Sp);
var folderID = getIdFromUrl(folderURL);
var folder = DriveApp.getFolderById(folderID);
// save zip file
var blob = response.getBlob();
var file = folder.createFile(blob);
file.setName(filename);
file.setDescription("Downloaded from " + url);
var fileID = file.getId();
Logger.log(fileID);
Logger.log(blob)
// extract zip (not working)
file.setContent('application/zip')
var fileUnzippedBlob = Utilities.unzip(file); // invalid argument error occurs here
var filename = 'unzipped file'
var fileUnzipped = folder.createFile(fileUnzippedBlob)
fileUnzipped.setName(filename)
}
else {
Logger.log(response.getResponseCode());
}
}
I've followed the instructions on the Utilities page. I can get their exact example to work. I've tried creating a .zip on my computer, uploading it to Google Drive and attempted to open it unsuccessfully. Obviously there are some subtleties of using the unzip that I'm missing.
Could you help me understand this?
I was running into the same "Invalid arguments" error in my testing, so instead of using:
file.setContent('application/zip')
I used:
file.setContentTypeFromExtension();
And, that solved the problem for me. Also, as #tukusejssirs mentioned, a zip file can contain multiple files, so unzip() returns an array of blobs (as documented here). That means you either need to loop through the files, or if you know you only have one, explicitly reference it's position in the array, like this:
var fileUnzipped = folder.createFile(fileUnzippedBlob[0])
Here's my entire script, which covers both of these issues:
/**
* Fetches a zip file from a URL, unzips it, then uploads a new file to the user's Drive.
*/
function uploadFile() {
var url = '<url goes here>';
var zip = UrlFetchApp.fetch('url').getBlob();
zip.setContentTypeFromExtension();
var unzippedFile = Utilities.unzip(zip);
var filename = unzippedFile[0].getName();
var contentType = unzippedFile[0].getContentType();
var csv = unzippedFile[0];
var file = {
title: filename,
mimeType: contentType
};
file = Drive.Files.insert(file, csv);
Logger.log('ID: %s, File size (bytes): %s', file.id, file.fileSize);
var fileId = file.id;
// Move the file to a specific folder within Drive (Link: https://drive.google.com/drive/folders/<folderId>)
var folderId = '<folderId>';
var folder = DriveApp.getFolderById(folderId);
var driveFile = DriveApp.getFileById(fileId);
folder.addFile(driveFile);
}
I think the answer to your question may be found here. Is there a size limit to a blob for Utilities.unzip(blob) in Google Apps Script?
If the download is over 100 mb the full file cannot be downloaded. Due to that it will not be in the proper zip format. Throwing the cannot unzip file error.
I believe that the creation of the blob from a file (in this case the .zip file) requires the .next(); otherwise it did not work for me.
Also note that the .zip file might contain more than one file, therefore I included a for cycle.
Anyway, my working/tested solution/script is the following:
function unzip(folderName, fileZipName){
// Variables
// var folderName = "folder_name";
// var fileZipName = "file_name.zip";
var folderId = getFolderId(folderName);
var folder = DriveApp.getFolderById(folderId);
var fileZip = folder.getFilesByName(fileZipName);
var fileExtractedBlob, fileZipBlob, i;
// Decompression
fileZipBlob = fileZip.next().getBlob();
fileZipBlob.setContentType("application/zip");
fileExtractedBlob = Utilities.unzip(fileZipBlob);
for (i=0; i < fileExtractedBlob.length; i++){
folder.createFile(fileExtractedBlob[i]);
}
}
This is a continuation from How to Use Advanced Drive Service to Upload Files.
My Webapp consists of an upload form for data files, which are then stored on Google Drive. (Full code in snippet below.)
I'm having a problem with the following line of code:
var file = Drive.Files.insert(resource, mediaData); // create file using Drive API
try {
//Get root folder and pull all existing folders, plus setup variables pulled from form
var dropbox = form.Country;
var timeStamp = new Date();
//Set file name slightly differently for Weekly Member Report (do not want to overright based on name just keep each extract so add timestamp to name)
if (form.reportType == "Member Weekly"){
var filename = form.reportType + timeStamp + ".xls";
}
else
{
var filename = form.reportType+".xls";
}
var rootfolder = DriveApp.getFolderById("0Byvtwn42HsoxfnVoSjB2NWprYnRiQ2VWUDZEendNOWwwM1FOZk1EVnJOU3BxQXhwU0pDSE0");
//Note root folder is Live Uploads Folder in Flatworld App folder structure
var folder, folders = rootfolder.getFoldersByName(dropbox);
//Check if folder exists and if not create
if (folders.hasNext()) {
folder = folders.next();
} else {
folder = rootfolder.createFolder(dropbox);
}
var FolderURL = folder.getUrl(); // Retain URL of folder for final end message to user
//Check if file already exists and delete if it does
var file, files = folder.getFilesByName(filename);
while( files.hasNext()){
file = files.next();
file.setTrashed(true);
}
//New Code from Stackover Flow:
//Upload file and set various properties
var mediaData = form.myFile;
var resource = {
title: filename,
mimetype: 'application/vnd.ms-excel',
description: "Uploaded via BNI Upload Form by " + form.myName + " on: " + timeStamp
};
var file = Drive.Files.insert(resource, mediaData); // create file using Drive API
var fileId = file.id;
var DriveAppFile = DriveApp.getFileById(fileId); // retrieve file in DriveApp scope.
var FileURL = DriveAppFile.getUrl(); //Retain URL of file for final end message to user
DriveApp.removeFile(DriveAppFile); // remove new file from Users root My Drive
folder.addFile(DriveAppFile); // puts file in selected folder
//End of New code from Stackover Flow
//Success message displayed to user
return "Thanks! File uploaded successfully to: <br><br><b>Folder Location:</b> " + FolderURL + "<br>" + "<b>File:</b> " + FileURL + ". <br><br>For any queries please email user#example.com copying the URLs displayed here in your email. You can close this browser window now or use the link below to upload another file.<br><br>";
} catch (error) {
//Catch error return it to user and email with error details
Its throwing the error message "Empty Response" on the line of code above when we try and upload a large file (15MB) Do you have any suggestions. This is well inside the Files insert limit of 5120GB and the code works fine on smaller files.
I've now tried to add in a loop in function to try the upload a couple of times, still throwing the same error sadly:
//setup function that will return null if file is not uploaded correctly
function createDriveFile(resource_f, mediaData_f){
try{
var file = Drive.Files.insert(resource_f, mediaData_f); // create file using Drive API
return file;
} catch (e) {return null;}
}
//try upload and loop if null
var maxTries = 3;
var tries = 0;
do {
tries = tries + 1;
if (tries > 0) {
var file = createDriveFile(resource, mediaData);
Logger.log("I'm trying to upload, try number: " + tries);
}
} while ((file == null) && (tries < maxTries));
if (file == null) {
var file = Drive.Files.insert(resource, mediaData); // try one laste time to create file using Drive API - outside loop function so if error is thrown script stops
}
The error only seems to occur on a larger file, even if we reduce the size of the same file that solves error so do we need to adjust the upload process to account for a larger file. Is there a Google Apps Script equivalent of making the API upload request resumable?
Your file size is the determinant factor here. Referencing the documentation suggests the simple upload method used here is good for up to 5MB only.
https://developers.google.com/drive/web/manage-uploads
Your comments seem to confirm this is what is happening for you.
As you hinted, use the resumable upload method. Use the uploadType: resumable parameter flag – API docs on the insert method describes how.
You can also check the naming of the file, for me I had a slash in the name which is why it would not upload. So take away any special characters before uploading and it should work.
I am trying to port the code below to DriveApp but the "doc.append" function does not work when I migrate to "DriveApp.append".
function process(thread, threadStart, folder, pass){
var start = Date.now();
var label = folder.getName();
var html;
if(pass > 1){
var docID = folder.createFile(label + "(part " + pass + ")", '<html>', MimeType.HTML).getFolderById();
}
else{
var docID = folder.createFile(label + "(part 1)", "<html>", MimeType.HTML).getFolderById();
}
var doc = DocsList.getFolderById(docID);
try{
doc.append(globalTOC(total_messages(thread), thread.length, label));
}
catch(exception){
Utilities.sleep(5000);
doc.append(globalTOC(total_messages(thread), thread.length, label));
}
The code you have posted has a few issues.
Firstly, it seems to be confusing Folders, Files and File IDs. The first part creates a File in a Folder, but then tries to call getFolderById(). Files do not have a method of this name. It then tries to acquire a File from that ID. If you want a File and its ID, you should just use the original File and call getId() on that:
var myDoc = folder.createFile(myFileName,
myHTMLcontents, MimeType.HTML);
var myDocID = myDoc.getId();
The above will work if you are using DriveApp Files or DocsList Files (which are different objects and should not be used interchangeably).
Secondly, there is currently no append() function available through DriveApp.File. If you need append functionality, one way to do it is to extract the File's contents as string, append to that string, and reset the contents with the new string:
var blob = doc.getBlob();
var content = blob.getDataAsString();
content += ' NEW CONTENT\n';
doc.setContent(content);
Note that setContent() will throw an exception if the content exceeds 10MB.
An alternate approach would be to build your file as a Google Doc, appending paragraphs as needed, and eventually covert that Doc to the file type you need.
Ryan Rith wrote 'DriveApp Files or DocsList Files are different objects and should not be used interchangeably'
Maybe the andwer I provided at migrating from docslist to driveapp can help while trying to port the code from DocsList to DriveApp
How do I locate the path of the current folder? I just want to be able to get the path of the folder so that I can manipulate the files in the folder without typing the path into the scripts.
For a Spreadsheet I found this to work:
thisFileId = SpreadsheetApp.getActive().getId();
var thisFile = DriveApp.getFileById(thisFileId);
var parentFolder = thisFile.getParents()[0].getName();
Thanks to Corey's answer and to Thomas'one, here is a "full featured" version that shows the folder tree in the logger and every parents id as well... just for fun ;-)
function getScriptFolderTree() {
var thisScript = getThisScriptInDrive();
var names = []
var Ids = []
var folder = thisScript.getParents()[0];
while (folder.getName() != "Root"){
names.unshift(folder.getName());
Ids.unshift(folder.getId());
var parents = folder.getParents();
var folder = parents[0];
}
Logger.log('Root/'+names.join().replace(/,/g,'/'))
Ids.unshift(DriveApp.getRootFolder().getId())
Logger.log(Ids)
}
function getThisScriptInDrive() {
return DriveApp.getFileById("poiuytrezazertyujhgfdsdcvcxyydryfhchfh");
}
(ID's are truncated intentionally)
Note that this script is working nicely but it strangely stops working if the 'random string' is modified... I imagine that the search engine in drive doesn't like the change but I have no serious explanation (comments welcome) but after a few minutes it works again ;-)
Add a function like this to your script
function getThisScriptInDrive() {
return DriveApp.find("some unique string that wont be anywhere else")[0];
}
This will search Drive and find this script itself because it contains that string - right there in the function call! - no need to declare it anywhere else. As long as you use an obscure enough string - i'd recommend mashing a few hundred chars on your keyboard - it will be unique across drive and therefore just work.
Once you have a File for this script, you can call getParents() etc.
I tweaked Serge's code to write a generic function
function getThisFilePath() {
//let thisFile = DriveApp.getFileById(DocumentApp.getActiveDocument().getId()); // for a Doc
let thisFile = DriveApp.getFileById(SpreadsheetApp.getActiveSpreadsheet().getId()); // for a Sheet
let path = {name: '', folder: []};
let folder = thisFile.getParents().hasNext() ? thisFile.getParents().next() : undefined;
while (folder) {
path.folder.unshift({name: folder.getName(), id: folder.getId()});
folder = folder.getParents().hasNext() ? folder.getParents().next() : undefined;
}
path.folder.forEach(folder => path.name += '/' + folder.name);
Logger.log(path.name);
return path;
}
Logging output
11:02:57 AM Info /My Drive/Technology/Development/2022
You could do this:
function myFunction() {
var thisScript = getThisScriptInDrive();
var folder = thisScript.getParents()[0];
while (folder.getName() != "Root"){
var parents = folder.getParents();
for (var i in parents){
var folder = parents[i];
Logger.log(folder.getName());
}
}
}
function getThisScriptInDrive() {
return DocsList.find("`<jj!?=(<DW+.W/m7SBF:sgu/#B(&Cs3:{ajA~ys#KmN4&]ujhpZ~z[Tv?+dk}MpK,8pY=w&dny8N'74:.9H:~uCgY=7pRt4[Tn5")[0];
}
This only works good if the folder has only 1 parent because it only takes 1 path.
edit: Thanks to Corey G
I was able to get the folder containing the script that was running:
//
// PrintScriptFolder -- print the name of the folder in which the running script resides.
//
function PrintScriptFolder()
{
var scriptId = ScriptApp.getScriptId();
console.info('scriptId = ' + scriptId);
var file = DriveApp.getFileById(scriptId);
var folders = file.getParents();
if (folders.hasNext())
{
var folder = folders.next();
var name = folder.getName();
console.info('script folder name = ' + name);
}
}
The trick is using ScriptApp to get the ID of the running script. From there it's pretty straightforward: use DriveApp to get the file ID, getParents() to get a list of (one) parent folder, next() to get it, and getName() to get its name.
First answer, long time lurker (be kind ;-)
Looking for a simple getPath, I went for a more generic approach, with limitations:
//simple test stub
function test_getFilePath() {
const scriptId = ScriptApp.getScriptId();
const fullPath = getFilePath(scriptId);
console.log(fullPath);
}
/**
* getFilePath
* #fileId id of file to find path for
*
* NB: google allows for multiple parents, which is a PITA
* so I'm just taking the first one I find up the tree
*/
function getFilePath(fileId) {
const file = DriveApp.getFileById(fileId);
const rootFolder = DriveApp.getRootFolder()
const rootFolderId = rootFolder.getId();
const rootFolderName = rootFolder.getName();
let folder = file.getParents().next();
let folderNames = [];
while (folder.getId() !== rootFolderId){
folderNames.unshift(folder.getName());
folder = folder.getParents().next();
}
const fullPath = rootFolderName+'/'+folderNames.join().replace(/,/g,'/');
return fullPath;
}
console:
11:36:40 AM Info My Drive/GAS
Had I more time/inclination, I'd have writen a recursive function to capture and return an array of possible parent paths, but there's no use case for me and who the hell wants to keep a the same file in multiple folders anyway??
(I'm sure there are use cases, eg, equivalent to using symlinks, but it stil makes me feel dirty ;-)