Google form file uploads: getting filenames - google-apps-script

Other users posted similar questions here and here. I tried to replicate the answer but received it as undefined. My users are permitted to upload up to 5 files, I would like to append the filenames as responses in (up to) 5 columns in a spreadsheet.
I presume that I would be able to get the filenames once I have the URLs using the following example.
function driveFile(){
//e.values[1] is apparently the file URL
var url = "https://drive.google.com/open?id=1bVbNAnES33oY4gx8npdp51ZSMekQalk"
var id = url.split("=")[1];
var file = DriveApp.getFileById(id);
var fileName = file.getName();
}
I tried this as suggested in the other question and it returned undefined.
I also tried using 0 as it made sense to get the URLs of the different files as 0 to 4.
var file = e.values[1]
I also tried the example from the other question but to no avail. I'm also not sure if it would return the URLs concatenated or in an array.
var fileBlob = e.parameter.thefile;
I would appreciate any guidance in this topic. Get URL → Filenames for (up to) 5 files submitted via Google Forms.

Answer:
This can be done using an onSubmit() trigger attached to the Google Form.
Further information:
The problem you are encountering is that values is not a parameter of the event object obtained on form submission - this is why you are getting an undefined return.
Form responses are a little more complicated than this - they are objects with predefined methods and in order to obtain the information out of them you need to use the methods provided in the Apps Script Documentation for Forms.
Code:
function onSubmit(e) {
// Assumiung the question which asks for the file upload is question 1
// Change this to the correct question number:
var uploadQuestionNumber = 1;
var formResponses = e.response.getItemResponses();
var names = [];
var fileUploads = formResponses[uploadQuestionNumber - 1];
for (i = 0; i < fileuploads.getResponse().length; i++) {
name.push(DriveApp.getFileById(fileUploads.getResponse()[i]));
}
return name;
}
Variable name will return an array of all the file names of the uploaded files. You can then use this to put into a Sheet using SpreadsheetApp.
References:
Google Forms events - Form submit
Class FormResponse
Class ItemResponse
Class DriveApp
getFileById(id) method
Class File

Related

getting Exception: Invalid ID error, when using getFileById

I am trying to use a script from a tutorial online that will put URLs in a google forms response sheet so I can go back and edit responses after they have been submitted.
The script comes from this guy https://www.youtube.com/watch?v=nqOE_FIMd_w
and his instructions are here: https://docs.google.com/document/d/1m9V_AHZdA24pUAR1xGxQNt_y3k7J9RKoSG5v_9oFvcU/edit
here is the script from the tutorial:
function assignEditUrls() {
var form = FormApp.openById('Your form ke goes here');
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Your responses Google Sheet name goes here - The tab name, not the file name');
var data = sheet.getDataRange().getValues();
var urlCol = Column number where URLs get entered goes here;
var responses = form.getResponses();
var timestamps = [], urls = [], resultUrls = [];
for (var i = 0; i < responses.length; i++) {
timestamps.push(responses[i].getTimestamp().setMilliseconds(0));
urls.push(responses[i].getEditResponseUrl());
}
for (var j = 1; j < data.length; j++) {
resultUrls.push([data[j][0]?urls[timestamps.indexOf(data[j][0].setMilliseconds(0))]:'']);
}
sheet.getRange(2, urlCol, resultUrls.length).setValues(resultUrls);
}
It seems all simple enough, but the script keeps giving me an error:
Invalid ID (line 2, file "getEditLinks")
Seems to be a problem with the document key.
I have already given the script permission and I have concentrated my attemtps to resolve this by grabbing different parts of this URL for the file. (I remember that Google used to need the whole URL at some point in the past)
Mine is
*https://docs.google.com/spreadsheets/d/1pKmad.....VtT3GaM/edit#gid=1905774080*
(where ..... is more of the doc key. I am not putting the whole lot for security reasons)
According to the tutorial, and all other research into this, it seems this part is the correct part from the URL to use.
1pKmad.....VtT3GaM
But this is what bring the error.
I tried the whole URL, the URL just up to the doc key part, and a few other subsets of this, but none work.
Can anyone see what I am doing wrong here?
Issue:
Based on your description you are trying to access a spreadsheet file by its id but you are using FormApp.
Instead you should be using SpreadsheetApp instead:
var form = SpreadsheetApp.openById('1pKmad.....VtT3GaM'); // form is now a spreadsheet object
but the new issue now is that the variable is not form anymore but a spreadsheet object and as a result you will get other errors down the road when calling form.getResponses().
Solution:
You need to use your existing script and put the id of the form instead of the spreadsheet, which can be found on the form url:
var form = FormApp.openById('Form ID here not Spreadsheet ID');
Note:
The form url looks like this:
https://docs.google.com/forms/d/formID/edit
and you can find the form that is attached to the spreadsheet file by the spreadsheet file menu:

Google Forms Rename "File upload" File to "Question - Submitter"

I am using Google Forms to collect images from team members and I want to ensure that each file that is uploaded to Google Forms and saved in Google Drive has the same naming convention.
There are five File upload questions that team members are asked to upload their images to. The files are placed into Google Drive folders with random file names followed by - firstName lastName. On an onFormSubmit trigger I would like to change the names of the user-provided files to fileUploadQuestionName - firstName lastName.
I am pretty new to Google Apps Script and I have no idea how to go about doing this. Any help would be greatly appreciated!
You can change the name of the uploaded file on each form submit by the following process
retrieve the last form response onFormSubmit with form.getResponses()[LAST FORM SUBMISSION]
retrieve the ID of the uploaded file with getItemResponses()[QUESTION NUMBER].getResponse()
open the file ID with DriveApp and change its name as desired
function myFunction() {
var form=FormApp.getActiveForm();
// returns the total number of form submissions
var length=form.getResponses().length;
//replace QUESTION NUMBER through the index of the question prompting the file upload - keep in mind that the first question has the index 0
var id=form.getResponses()[length-1].getItemResponses()[QUESTION NUMBER].getResponse();
//getResponses()[length-1] retrieves the last form response, accounting for the fact that the first index is zero and hte last length-1
//gets the name of the question
var fileUploadQuestionName=form.getResponses()[length-1].getItemResponses()[QUESTION NUMBER].getItem().getTitle();
//accesses the uploaded file
var file=DriveApp.getFileById(id);
name = file.getName();
//changes the file name
var name = fileUploadQuestionName+' - '+name.split(' - ')[1]
file.setName(name);
}
PS: If you want to change a posteriori the names of all the files submitted and not just the last files - you need to loop through all form responses:
for(var i=0;i<length;i++){
var id=form.getResponses()[i].getItemResponses()[QUESTION NUMBER].getResponse();
...
...
}
The easiest way to do this would likely be by either iterating through the specified folder in google drive on a time-based trigger and either checking if they meet your specified condition or moving them to a different folder after they're renamed.
function checkFile()
{
var files = DriveApp.getFolderById('ID of Folder').getFiles();
// you can find this by going to form responses and clicking the link to an attached file and copying the id from the URL
// https://drive.google.com/drive/folders/xxxxxxxxxxxxxxxxxxxxxxxxxxx
var fileUploadQuestionName = 'Test';
while(files.hasNext())
{
var file = files.next();
var name = file.getName();
if(name.indexOf(fileUploadQuestionName) == -1)
{
name = fileUploadQuestionName+' - '+name.split('-')[1]
file.setName(name);
}
}
}
From there you'll need to add a time-based trigger to run every hour or day or minute depending on how critical it is to always find files of the correct name.
I can't find any documentation on accessing the file from within the response item on google's formApp documentation.

Efficient Way of sending Spreadsheet over email using GAS function?

I am creating an addon for Google Sheets that my local High School's volunteer clubs can use to keep track of their member's volunteer hours. Most of the code is done and works very nicely, and I am currently working on a system that will send a member a spreadsheet listing all of the volunteer events that they have logged. I have GAS create a separate spreadsheet, and then send an email with that separate spreadsheet attached in PDF. When the email is received, the PDF is empty except for a singular empty cell at the top left of the page.
I am pretty new to GAS but have been able to grasp the content pretty easily. I have only tried one method of sending the Spreadsheet and that is by using the .getAs(MimeType.PDF). When I changed the "PDF" to "GOOGLE_SHEETS," GAS returned the error: "Blob object must have non-null data for this operation." I am not entirely sure what a Blob object is, and have not found any website or video that has fully explained it, so I am not sure how to go about troubleshooting that error.
I think I'm having a problem grabbing the file because it either sends an empty PDF or it returns an error claiming it needs "non-null data."
function TigerMail()
{
var Drive = DriveApp;
var app = SpreadsheetApp;
var LOOKUP = app.getActiveSpreadsheet().getSheetByName("Student
Lookup");
var Name = LOOKUP.getRange("E1").getValue();
Name = Name + "'s Hours";
//app.openById(Name+"'s Hours");
var HOURS = app.create(Name);
var ESheet = HOURS.getSheets()[0];
var ROW = LOOKUP.getLastRow();
var arr = LOOKUP.getRange("D1:J"+ROW).getValues();
var cell = ESheet.getRange("A1:G"+ROW);
cell.setValues(arr);
////////////////////////////////////////////////////
var LOOKUP = app.getActiveSpreadsheet().getSheetByName("Student
Lookup");
var cell = LOOKUP.getRange("D1");
var Addr = cell.getValue();
var ROW = LOOKUP.getLastRow();
var file = Drive.getFilesByName(Name);
var file = file.next();
var FORMAT = file.getAs(MimeType.GOOGLE_SHEETS);
TigerMail.sendEmail(Addr, "Hours", "Attached is a list of all of the
events you have volunteered at:", {attachments: [FORMAT]} );
}
the final four lines are where the errors are occurring at. I believe I am misunderstanding how the .next() and .getFilesByName() work.
(above the comment line: creating a spreadsheet of hours)
(below the comment line: grabbing the spreadsheet and attaching it to an email)
Here is the link to the Google Sheet:
https://docs.google.com/spreadsheets/d/1qlUfTWaj-VyBD2M45F63BtHaqF0UOVkwi04XwZFJ4vg/edit?usp=sharing
In your script, new Spreadsheet is created and put values.
You want to sent an email by attaching the file which was converted from the created Spreadsheet to PDF format.
If my understanding is correct, how about this modification? Please think of this as just one of several answers.
Modification points:
About Drive.getFilesByName(Name), unfortunately, there is no method of getFilesByName() in Drive.
I think that when you want to use the created Spreadsheet, HOURS of var HOURS = app.create(Name) can be used.
About var FORMAT = file.getAs(MimeType.GOOGLE_SHEETS), in the case of Google Docs, when the blob is retrieved, the blob is automatically converted to PDF format. This can be also used for your situation.
In order to save the values put to the created Spreadsheet, it uses SpreadsheetApp.flush().
When above points are reflected to your script, it becomes as follows.
Modified script:
Please modify as follows.
From:
var file = Drive.getFilesByName(Name);
var file = file.next();
var FORMAT = file.getAs(MimeType.GOOGLE_SHEETS);
To:
SpreadsheetApp.flush();
var FORMAT = HOURS.getBlob();
Note:
In your script, it seems that var ROW = LOOKUP.getLastRow() is not used.
References:
flush()
getBlob()
If I misunderstood your question and this was not the result you want, I apologize.

Showing thumbnails in a Google sheet [duplicate]

This question already has answers here:
How to get the file URL from file name in Google Sheets with correct Authorization via custom function/script
(3 answers)
Closed 4 years ago.
The code below generates the IMAGE function for the sheet to show thumbnails of all (PDF) files in a chosen folder, obtained with a URL and the file ID:
function scannedMail() {
var files, file, sheet;
sheet = SpreadsheetApp.getActive().getSheetByName('ScannedMail');
files = DriveApp.getFoldersByName("ScannedMail").next().searchFiles('');
var i = 1;
while (files.hasNext()) {
var file = files.next();
var ID = file.getId();
sheet.getRange('A' + i).setValue("=IMAGE(\"https://drive.google.com/thumbnail?authuser=0&sz=w320&id=" + ID + "\"\)");
sheet.getRange('B' + i).setValue(file.getName());
i=i+1;
}
}
Yet it does not show the thumbnails. I found out that it shows just the ones where I manually retrieved the ID from getting a "shareable link". Apparently this ensures the right share settings to get the thumbnails of my own files.
1) Is the previous assumption correct, and why do I need to adapt share settings somehow, where I have read other files without any issues?
2) How can I adapt the script to adapt the share settings, or make it work otherwise?
The script is meant to operate just within my own Google account, and to keep the files private.
I tried sharing the folder with myself, but that does not make a difference (or sense). Is the script somehow regarded as being another user than myself?
Following suggestions from #Rubén and #Cooper, I have tried using insertImage either based on a URL:
sheet.insertImage(file.thumbnailLink, 1, i)
or based on a blob:
sheet.insertImage(file.getThumbnail(), 1, i)
But the most I could get out of Google was "We're sorry, a server error occurred. Please wait a bit and try again", with the code below:
function ScannedMail() {
var files, file, , name, blob, sheet;
sheet = SpreadsheetApp.getActive().getSheetByName('ScannedMail');
files = DriveApp.getFoldersByName("ScannedMail").next().searchFiles('');
var i = 1;
while (files.hasNext()) {
file = files.next();
name = file.getName(); //not needed, just for debugging
blob = file.getThumbnail();
sheet.insertImage(blob, 1, i); // it runs up to here...
i = i + 1;
}
}
The code execution gets stuck on the first occurrence of insertImage().
So we have 3 approaches (IMAGE function in sheet, insertImage(URL,1,1), and insertImage(blob,1,1)) but all 3 do not make a thumbnail appear, apart from the first method when you make the file public (not a serious option).
I don't see a duplicate question and answer that helps me find out what is wrong with my code, or helps me to somehow get the required thumbnails in the spreadsheet. The kindly proposed solutions did not succeed in that yet.
Try something like this:
function imgArray() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('ImageArray');
if(!sh){
sh=ss.insertSheet('ImageArray');
}
var imgA=[];
var folder=DriveApp.getFolderById('folderid');
var files=folder.getFiles();
while(files.hasNext()){
var file=files.next();
var filename=file.getName();
imgA.push(file.getBlob());
}
for(var r=0;r<imgA.length;r++){
sh.insertImage(imgA[r],1,r+1);
}
}
This was adapted from an answer from #Tanaike.
I guess this is what you were looking for:
function ScannedMail() {
var sheet = SpreadsheetApp.getActive().getSheetByName('ScannedMail');
var files = DriveApp.getFoldersByName("ScannedMail").next().searchFiles('');
var i = 1;
while (files.hasNext()) {
var file = files.next();
var blob = file.getBlob();
sheet.insertImage(blob, 1, i); // it runs up to here...
i = i + 1;
}
}
Google Sheets IMAGE built-in function only is able to retrieve images that are publicly available, so, yes you should have to adapt the sharing settings to make the images viewable by anyone.
In order to keep the files private you should not use IMAGE built-in function, you could use one of the methods of Class Sheet to insert images like insertImage(blobSource,column,row). See answer to Adding Image to Sheet from Drive for an example.
NOTES:
As custom functions run anonymously they can't be used them either.
According to Mogsdad answer to InsertImage(url,x,y) doesn't work with Google Drive insertImage(url,row,column) can't be used to insert images from Google Drive
Related
Image function or .insertImage not working for Google Apps Script and Sheets
What is the right way to put a Drive image into a Sheets cell programmatically?

When using the FormResponse class, a unexpected Id is returned for a response depending on how the response is retrieved

I'm hoping someone has seen this before, I'm using Google Forms, and on submission, using Google Apps Script to store the response id in a separate Spreadsheet in order to store further meta data in the future (Id gained from the method in the onSubmit event response e.response.getId()). Upon the first submission of a Google Form, I logged the response ID in a new sheet.
I can load the response through form.getResponse(responseIdStoredInSheet), however, when I loop through the responses returned by form.getResponses() and call .getId() on those FormResponses returned, the Id is slightly altered at the end. I've searched for an afternoon around the docs and online as to why this happens, but to no avail.
function testResponseIds()
{
var responseIdStoredInSheet = 'ChMxNzM4MDQzNzQ5MjQyNDc0Njg4EAA';
//only response in the form
var formResponse = form.getResponse(responseIdStoredInSheet);
//outputs as expected above - ChMxNzM4MDQzNzQ5MjQyNDc0Njg4EAA
Logger.log(formResponse.getId());
//loop through all responses (only above response is present)
var formResponses = form.getResponses();
for (var i = 0; i < formResponses.length; i++)
{
//Outputs slightly different Id - ChMxNzM4MDQzNzQ5MjQyNDc0Njg4EJ2kvMHLzsOvdg
Logger.log(formResponses[i].getId());
}
}
Logged output from above:
[14-03-03 17:12:51:259 GMT] ChMxNzM4MDQzNzQ5MjQyNDc0Njg4EAA
[14-03-03 17:12:51:279 GMT] ChMxNzM4MDQzNzQ5MjQyNDc0Njg4EJ2kvMHLzsOvdg
I tested your code on one of my form and it look to be working as intented. My supposition is that you deleted the answer you are trying to retrieve but you can still get it through the script(I don't know the reason, the cache I suppose).
What I can propose you is to retrieve the edit URL so you will see that it's not the same answer (modifying it and checking the form answers).
Logger.log(formResponses[i].getEditResponseUrl());
By the way in your posted script you forgot:
var form = FormApp.getActiveForm();
or the equivalent (couldn't run your script without adding that line)