Google script save / update file - google-apps-script

I am again creating a simple script to save a daily menu cart. Aim is, that the file (PDF) can be shared with a static link. Base for the PDF is a Google spreadsheet.
Currently I have the following code:
// Add new menu to sheet
function onOpen() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [
{name: "Speichern", functionName: "savepdf"},
];
spreadsheet.addMenu("Als PDF speichern", entries);
};
// Add function to save sheet as PDF
function savepdf () {
// Get spreadsheet file
var fileid = 'FILEIDGOESHERE';
// Create date for file name
var ss = SpreadsheetApp.openById(fileid);
var name = ss.getName();
var sheet = ss.getSheetByName('Tageskarte');
var range = sheet.getRange(12,1);
var d = range.getValue();
var curr_date = d.getDate();
var curr_month = d.getMonth() + 1;
var curr_year = d.getFullYear();
var theDate = curr_year + "-" + curr_month + "-" + curr_date + "-";
var namearchive = "Tageskarte-"+ theDate +".pdf";
var name = "Tageskarte-Allweglehen.pdf";
// Choose folder where PDFs are saved
var foldersave=DriveApp.getFolderById('FOLDERIDGOESHERE');
var foldersavearchive=DriveApp.getFolderById('FOLDERIDGOESHERE');
// OAuth
var request = {
"method": "GET",
"headers":{"Authorization": "Bearer "+ScriptApp.getOAuthToken()},
"muteHttpExceptions": true
};
// Create PDF + update current file
var fetch='https://docs.google.com/spreadsheets/d/'+fileid+'/export?format=pdf&size=A4&portrait=true&gridlines=false'
var pdf = UrlFetchApp.fetch(fetch, request);
pdf = pdf.getBlob().getAs('application/pdf').setName(name);
var file = foldersave.createFile(pdf);
// Create PDF for archive and save
var pdfarchive = UrlFetchApp.fetch(fetch, request);
pdfarchive = pdfarchive.getBlob().setName(namearchive);
var file = foldersavearchive.createFile(pdfarchive);
}
/*
fmcmd=12
size=legal/A4
fzr=true/false
portrait=false/true
fitw=true/false
gid=0/1/2
gridlines=false/true
printtitle=false/true
sheetnames=false/true
pagenum=UNDEFINED
attachment=false/true
*/
My problem is the point "Create PDF + update current file. The code is saving a new file with the same name, but than I have of course a new static share link of the menu.
I think I have to use something with the "getblob" function to update the current file.
Would be very good, if anybody would have an idea.
Many thanks.

I was able to find a working solution with Drive.Files.update
// Create PDF + update current file
var fetch='https://docs.google.com/spreadsheets/d/'+fileid+'/export?format=pdf&size=A4&portrait=true&gridlines=false'
var pdf = UrlFetchApp.fetch(fetch, request);
pdf = pdf.getBlob().getAs('application/pdf').setName(name);
var deleteexisting = foldersave.getFilesByName(name);
if (deleteexisting.hasNext() === false) {
// if no file is found then create it
foldersave.createFile(pdf);
} else {
while (deleteexisting.hasNext()) {
var updatedPDF = deleteexisting.next();
Drive.Files.update({mimeType: 'application/pdf'}, updatedPDF.getId(), pdf);
}
}

Related

I want to change the names of old files uploaded to Google Forms

I have a google form and all the files uploaded by the participants contain random names.
The data is summarized in personal photos, ID photos, photos of certificates.
That is why we want to name the files with the names of the people themselves by choosing a specific field.
I tried in a code via the google apps script to change the names but it is no more than 70 to 80 files and then it stops
Is there a code that will help me with this work, especially since the number of files is large?
function renamefile() {
var form = FormApp.openById('16aPNgaSuDZ-e0ibw08jW7_-YMULGWV-_iUP9uQHE'); //DDD
var formResponses = form.getResponses();
var baseString = 'https://drive.google.com/file/d/';
var endString = '/view?usp=drivesdk';
var folder = DriveApp.getFolderById('1BPA7wkECV1vOB6bo3tGfUZd9MifjwYJG2BVI_sHSeNojFY-xiN4dvLW_vc2zw4IEe');
var files = folder.getFiles();
while (files.hasNext()) {
var file = files.next();
for (var i = 0; i < formResponses.length; i++) {
var formResponse = formResponses[i];
var itemResponses = formResponse.getItemResponses();
var itemResponseFname = itemResponses[1];
var itemResponsePhoto = itemResponses[15];
var photoID = itemResponsePhoto.getResponse();
var newName = itemResponseFname.getResponse() + " - " + "PHOTO" ;
var url = baseString + photoID + endString;
var urlCheck = file.getUrl();
if ( url == urlCheck) {
var modName = newName + ".pdf" ;
file.setName(modName);
}
}
}
}
Modification points:
From your showing script, it seems that the form responses are retrieved every loop of the "while loop". I thought that this might be a high process cost.
From your script, it seems that you want to rename PDF files. In this case, when PDF files are retrieved. The process cost might be able to be reduced.
I thought that in your situation, the file ID can be used for checking the file instead of URL.
When these points are reflected in your script, how about the following modification?
Modified script:
function renamefile() {
var form = FormApp.openById('###'); // Please set your file ID of Google Forms.
var folder = DriveApp.getFolderById('###'); // Please set your folder ID.
// Create an object for searching file ID.
var obj = form.getResponses().reduce((o, f) => {
var itemResponses = f.getItemResponses();
var itemResponseFname = itemResponses[1];
var itemResponsePhoto = itemResponses[15];
var fileId = itemResponsePhoto.getResponse();
var newName = itemResponseFname.getResponse() + " - " + "PHOTO";
o[fileId] = newName + ".pdf";
return o;
}, {});
// Rename files.
var files = folder.getFilesByType(MimeType.PDF);
while (files.hasNext()) {
var file = files.next();
var fileId = file.getId();
if (obj[fileId]) {
file.setName(obj[fileId]);
}
}
}
When this script is run, first, an object is created. This object is used for searching the file ID in the folder. And, using the object, the PDF files in the folder are renamed.
Note:
If your files are not PDF files, please modify var files = folder.getFilesByType(MimeType.PDF); to var files = folder.folder.getFiles();.
When the number of files is large and the processing time is over 6 minutes, I would like to propose using the batch requests. The sample script of batch request can be seen at this thread. And, when the above script is modified, the sample script is as follows. When you use this script, please enable Drive API at Advanced Google services. And also, please install a Google Apps Script library of BatchRequest. Ref
function renamefile2() {
var form = FormApp.openById('###'); // Please set your file ID of Google Forms.
var folderId = "###"; // Please set your folder ID.
// Create an object for searching file ID.
var obj = form.getResponses().reduce((o, f) => {
var itemResponses = f.getItemResponses();
var itemResponseFname = itemResponses[1];
var itemResponsePhoto = itemResponses[15];
var fileId = itemResponsePhoto.getResponse();
var newName = itemResponseFname.getResponse() + " - " + "PHOTO";
o[fileId] = newName + ".pdf";
return o;
}, {});
// Rename files using batch request.
var { items } = Drive.Files.list({ q: `'${folderId}' in parents and trashed=false and mimeType='${MimeType.PDF}'`, maxResults: 1000, fields: "items(id)" });
var requests = items.reduce((ar, { id }) => {
if (obj[id]) {
ar.push({
method: "PATCH",
endpoint: `https://www.googleapis.com/drive/v3/files/${id}`,
requestBody: { name: obj[id] },
});
}
return ar;
}, []);
var result = BatchRequest.EDo({ batchPath: "batch/drive/v3", requests });
}
In this sample, it supposes that the number of your files is less than 1000. When the number of files is more than 1000, please retrieve all files using pageToken.
Reference:
reduce()

Half of information uploaded, only images no data in google sheets

I created an app that at the same time sends data to a google sheet and image to a drive where the uploaded image URL is displayed alongside the data sent.
My issue is that the image is uploaded properly with the date and time in the title but my data aren't sent to the google script.
I tried but didn't found any workable solution.
here is my code
function doGet(e) {
SpreadsheetApp.openById('XXXXXXXXXXXXXXX').getSheetByName("Test_Sheet");
return addUser(e);
}
function doPost(e) {
SpreadsheetApp.openById('XXXXXXXXXXXXXXX').getSheetByName("Test_Sheet");
return addUser(e);
}
function addUser(e) {
var tag1 = e.parameter.tag1;
var tag2 = e.parameter.tag2;
var nameTag = e.parameter.nameTag;
var mimetypeTag = e.parameter.mimetypeTag;
var dataTag = e.parameter.dataTag;
var filename = nameTag + Utilities.formatDate(new Date(), "GMT+4", "dd-MM-yyyy HH:mm");
var data = Utilities.base64Decode(dataTag);
var blob = Utilities.newBlob(data, mimetypeTag, filename);
var file = DriveApp.getFolderById('XXXXXXXXXXXXXXX').createFile(blob).getID();
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
var fileId = file.getId();
var fileUrl = "https://drive.google.com/uc?export=view&id=" + fileId;
var sheet = SpreadsheetApp.openById('16T_51Xtjm5udbYBAEfSbNpo--ox9PQZddT39Ty0Zky8').getSheetByName("Test_Sheet");
sheet.appendRow([tag1, tag2, nameTag, mimetypeTag, dataTage, filename, fileUrl])
return ContentService.createTextOutput("Data uploaded");
}
Replace dataTage with dataTag.

Export Google Sheets to PDF without cell gridlines

I'm trying to export PDF from Google Sheets via Apps Script. I've found useful code online, which works perfectly, except I can't find a way to export it without the grid, or change margins and page size.
function generatePdf() {
var originalSpreadsheet = SpreadsheetApp.getActive();
var sourcesheet = originalSpreadsheet.getSheetByName("TestSheet");
var sourcerange = sourcesheet.getRange('B1:K55'); // range to get - here I get all of columns which i want
var sourcevalues = sourcerange.getValues();
var data = sourcesheet.getDataRange().getValues();
var number = originalSpreadsheet.getRange('G9:H9').getValue();
var newSpreadsheet = SpreadsheetApp.create("Invoice pdf"); // can give any name.
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var projectname = SpreadsheetApp.getActiveSpreadsheet();
var sheet = sourcesheet.copyTo(newSpreadsheet);
var destrange = sheet.getRange('B1:K55');
destrange.setValues(sourcevalues);
newSpreadsheet.getSheetByName('Sheet1').activate();
newSpreadsheet.deleteActiveSheet();
var invoiceName = "Invoice "+number;
var pdf = DriveApp.getFileById(newSpreadsheet.getId());
var theBlob = pdf.getBlob().getAs('application/pdf').setName(invoiceName);
var folderID = "1Y7n1e_tzQWvzVykHJf_DSm9lBVmokDHA"; // Folder id to save in a folder.
var folder = DriveApp.getFolderById(folderID);
var newFile = folder.createFile(theBlob);
DriveApp.getFileById(newSpreadsheet.getId()).setTrashed(true);
}
I've been looking for answers everywhere, but I cannot apply some of the solutions I find to my code.
I'm not sure how to do it with the code, but if you want to do it without code... when downloading from Google Sheets, you have the option to remove the cell gridlines. Here are the directions:
File > Download > PDF Document (.pdf)
This will open a preview screen. Sidebar on the right has a dropdown menu called "Formatting."
Under "Formatting," uncheck the box labeled "Show gridlines."
Thats it!
It’s not possible to pass options into the getAs function, but you can export the file yourself and download the URL:
function getPdf(spreadsheet) {
var options = {
format: 'pdf',
exportFormat: 'pdf',
size: 7, // A4
portrait: true,
gridlines: false,
};
// construct export URL
var query = Object.keys(options).map(function (key) {
return encodeURIComponent(key) + '=' + encodeURIComponent(options[key]);
}).join('&');
var exportUrl = spreadsheet.getUrl().replace(/\/edit.*$/, '/export?' + query);
var response = UrlFetchApp.fetch(exportUrl, {
headers: {
Authorization: 'Bearer ' + ScriptApp.getOAuthToken(),
},
});
return {
fileName: spreadsheet.getName() + '.pdf',
content: response.getBlob().getBytes(),
mimeType: MimeType.PDF,
};
}
The full list of export options can be found at https://stackoverflow.com/a/46312255.

Prevent Renaming of Google Forms File Upload

I have a Google Form that uses the File Upload feature that is mentioned on the below site,
https://sites.google.com/site/scriptsexamples/home/announcements/google-forms-file-upload-feature
I am wondering 2 things:
Is there a way to prevent the renaming of the uploaded file. (IE: original_file_name - Name of Uploader)
Concerning the below quote:
If a respondent decides to upload a file directly from his Drive, a
copy will be made and the form owner will become owner of the copy.
Is it possible to identify the original file and remove it so that it does not junk up the uploaders root Drive folder.
function getCurrentResponses() {
var form = FormApp.openById('formID');
var formResponses = form.getResponses();
var numResponses = formResponses.length;
var lastResponse = formResponses[numResponses - 1];
var lastResponseItem = lastResponse.getItemResponses();
var emailAddress = lastResponseItem[0].getResponse();
Logger.log('emailAddress: ' + emailAddress);
var zone = lastResponseItem[1].getResponse();
Logger.log('zone: ' + zone);
var projectName = lastResponseItem[2].getResponse();
Logger.log('projectName: ' + projectName);
var igeURL = lastResponseItem[3].getResponse();
Logger.log('igeURL: ' + igeURL);
//Creating folder for submitted project
var DriveFolder = DriveApp.getFolderById("destinationFolder");
var folderName = projectName;
//create the folder
var folderpath = DriveFolder.createFolder(folderName).addEditor(emailAddress).getId();
//get the path to the folder
var pathtoemail = "https://drive.google.com/drive/folders/"+folderpath;
var file = DriveApp.getFileById(igeURL);
file.getParents().next().removeFile(file);
DriveApp.getFolderById(folderpath).addFile(file);
var fileName = file.getName();
//Create the message and subject of the email
var message = 'Thank you for submitting. Here is the link to your drive folder. Please use this folder to upload and receive related documentation. ' +pathtoemail ; //a custom message, feel free to change anything inside the quotes
var subject = "Form Related Documentation." ;
//send the email
MailApp.sendEmail(emailAddress, subject, message, {
// cc: 'email#email.com' // optional cc
});
/*
* Convert Excel file to Sheets
* https://gist.github.com/azadisaryev/ab57e95096203edc2741
* Retrieved 06/08/2017
*/
var xlsId = igeURL; // ID of Excel file to convert
var xlsFile = DriveApp.getFileById(xlsId); // File instance of Excel file
var xlsBlob = xlsFile.getBlob(); // Blob source of Excel file for conversion
var xlsFilename = xlsFile.getName(); // File name to give to converted file; defaults to same as source file
var destFolders = folderpath; // array of IDs of Drive folders to put converted file in; empty array = root folder
Logger.log("destFolders: " + destFolders);
var ss = convertExcel2Sheets(xlsBlob, xlsFilename, destFolders);
var ssID = ss.getId();
Logger.log(ss.getId());
var convertFile = DriveApp.getFileById(ssID);
convertFile.getParents().next().removeFile(convertFile);
DriveApp.getFolderById(folderpath).addFile(convertFile);
}
/**
* Convert Excel file to Sheets
* https://gist.github.com/azadisaryev/ab57e95096203edc2741
* Retrieved 06/08/2017
* #param {Blob} excelFile The Excel file blob data; Required
* #param {String} filename File name on uploading drive; Required
* #param {Array} arrParents Array of folder ids to put converted file in; Optional, will default to Drive root folder
* #return {Spreadsheet} Converted Google Spreadsheet instance
**/
function convertExcel2Sheets(excelFile, filename, arrParents) {
var parents = arrParents || []; // check if optional arrParents argument was provided, default to empty array if not
if ( !parents.isArray ) parents = []; // make sure parents is an array, reset to empty array if not
Logger.log("arrParents: " + arrParents);
// Parameters for Drive API Simple Upload request (see https://developers.google.com/drive/web/manage-uploads#simple)
var uploadParams = {
method:'post',
contentType: 'application/vnd.ms-excel', // works for both .xls and .xlsx files
contentLength: excelFile.getBytes().length,
headers: {'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()},
payload: excelFile.getBytes()
};
// Upload file to Drive root folder and convert to Sheets
var uploadResponse = UrlFetchApp.fetch('https://www.googleapis.com/upload/drive/v2/files/?uploadType=media&convert=true', uploadParams);
// Parse upload&convert response data (need this to be able to get id of converted sheet)
var fileDataResponse = JSON.parse(uploadResponse.getContentText());
// Create payload (body) data for updating converted file's name and parent folder(s)
var payloadData = {
title: filename,
parents: []
};
if ( parents.length ) { // Add provided parent folder(s) id(s) to payloadData, if any
for ( var i=0; i<parents.length; i++ ) {
try {
var folder = DriveApp.getFolderById(parents[i]); // check that this folder id exists in drive and user can write to it
payloadData.parents.push({id: parents[i]});
}
catch(e){} // fail silently if no such folder id exists in Drive
}
}
// Parameters for Drive API File Update request (see https://developers.google.com/drive/v2/reference/files/update)
var updateParams = {
method:'put',
headers: {'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()},
contentType: 'application/json',
payload: JSON.stringify(payloadData)
};
// Update metadata (filename and parent folder(s)) of converted sheet
UrlFetchApp.fetch('https://www.googleapis.com/drive/v2/files/'+fileDataResponse.id, updateParams);
return SpreadsheetApp.openById(fileDataResponse.id);
}
I just wanted to post that I was able to create a work-around for the renaming of the file.
var form = FormApp.openById('formID');
var formResponses = form.getResponses();
var numResponses = formResponses.length;
var lastResponse = formResponses[numResponses - 1];
var lastResponseItem = lastResponse.getItemResponses();
var submittedID = lastResponseItem[3].getResponse();
var file = DriveApp.getFileById(submittedID);
var oldFileName = file.getName();
var indexOldFileName = oldFileName.indexOf("-");
var newFileName = oldFileName.slice(0,indexOldFileName - 1);
var indexOldFileExt = oldFileName.lastIndexOf(".");
var newFileExt = oldFileName.slice(indexOldFileExt);
var modName = newFileName + newFileExt;
file.setName(modName);
As for my second question, I still haven't been able to solve it. I will post if I find a solution.
I change your code a little bit so the name of the file is changed to its ID.
function changenametoid() {
var form = FormApp.openById('formID');
var formResponses = form.getResponses();
var numResponses = formResponses.length;
var lastResponse = formResponses[numResponses - 1];
var lastResponseItem = lastResponse.getItemResponses();
var submittedID = lastResponseItem[0].getResponse();
var file = DriveApp.getFileById(submittedID);
var oldFileName = file.getName();
var indexOldFileName = oldFileName.indexOf("-");
var newFileName = submittedID;
var indexOldFileExt = oldFileName.lastIndexOf(".");
var newFileExt = oldFileName.slice(indexOldFileExt);
var modName = newFileName + newFileExt;
file.setName(modName);
}

Downloading a PDF version of the open spreadsheet in Google Drive via scripts

I've been reading up on how to save a spreadsheet to PDF via Google Docs Scripting. Most suggestions I've come across reference using something like:
theOutputFile.saveAndClose();
DocsList.createFile(theOutputFile.getAs('application/pdf')).rename(theOutputName+".pdf");
That is, they reference the saveAndClose() function. I don't want to save or close my spreadsheet - but I do want to download the current sheet as a PDF.
Any suggestions? Thanks.
For saving the current sheet as a PDF, you can hide all the other sheets, save the current, & then show all sheets again.
The pdf creation might start before the end of the sheets' hiding and then will include 2 sheets - the current & the last sheets - in the pdf file.
Adding a sleep or a confirmation msgbox, between showOneSheet & createPdf eliminated the problem.
This answer is a variation of Marco Zoqui's answer: "To send a single sheet you may hide all other before sending" in Google Apps Script to Email Active Spreadsheet
var sheet = SpreadsheetApp.getActiveSheet();
var sheetToSave = sheet.getName();
showOneSheet(sheetToSave);
Utilities.sleep(2000);
createPdf("TestFolder", "TestPDF");
showAllSheets();
function showOneSheet(SheetToShow) {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
for(var i in sheets){
if (sheets[i].getName()==SheetToShow){
sheets[i].showSheet();
}
else {
sheets[i].hideSheet();
}
}
}
function showAllSheets() {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
for(var i in sheets){
sheets[i].showSheet();
}
}
function createPdf(saveToFolder, fileName){
var ssa = SpreadsheetApp.getActiveSpreadsheet();
var pdf = ssa.getAs("application/pdf");
try {
var folder = DocsList.getFolder(saveToFolder);
}
//Create Folder if not exists
catch(error){
folder = DocsList.createFolder(saveToFolder);
}
var file = folder.createFile(pdf);
file.rename(fileName);
return file;
}
I was able to get it to work using #hsgv's answer, however, this is the version I ended up using based on this.
// global save to folder variable:
var folderName = "My/Special/Folder";
function createInvoiceInGoogleDrive(){
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getActiveSheet();
// getting some values from the spreadhseet for the file name
var invoiceNumber = sheet.getRange("E3").getValue();
var vendor = sheet.getRange("A9").getValue();
var fileName = invoiceNumber + ' - ' + vendor + " - Invoice.pdf";
var pdfBlob = sheetToPDF(spreadsheet, sheet);
pdfBlob.setName(fileName);
var folder = getOrCreateFolder(folderName);
var matchingFileList = folder.find(fileName);
if ( matchingFileList.length > 0 ) {
Browser.msgBox("ERROR: New invoice not created. " + fileName + " already exists at " + folderName);
return false;
} else {
var f = folder.createFile(pdfBlob);
spreadsheet.toast('Created a new invoice on Google Drive!');
return true;
}
}
// thanks: https://gist.github.com/gregorynicholas/9008572
function sheetToPDF(spreadsheet, sheet) {
var ssID = spreadsheet.getId();
var gid = sheet.getSheetId();
// &gid=x at the end of above url if you only want a particular sheet
var url2 = "http://spreadsheets.google.com/feeds/download/spreadsheets/Export?key=" + ssID +
"&gid=" + gid +
"&fmcmd=12&size=7&fzr=true&portrait=true&fitw=true&locale=en&gridlines=false&printtitle=false&sheetnames=false&pagenum=UNDEFINED&attachment=true";
// AUTH TOKEN required to access the UrlFetchApp call below. You can receive it
// from https://appscripts.appspot.com/getAuthToken
var AUTH_TOKEN = "{GET YOUR OWN AUTH TOKEN}";
var auth = "AuthSub token=\"" + AUTH_TOKEN + "\"";
var res = UrlFetchApp.fetch(url2, {headers: {Authorization: auth}}).getBlob();
return res;
}
/**
* Get or create a folder based on its name/path
*/
function getOrCreateFolder(folderName) {
try {
var theFolder = DocsList.getFolder(folderName);
} catch(error){
var theFolder = DocsList.createFolder(folderName);
}
return theFolder;