I've created a custom Google Form which allows a logged in Google user to include multiple files with the form.
The files are uploaded into a newly created folder in Google Drive.
The Google Script uses the current user's email address + current date and time as the folder name.
Problem and expected result:
Because of the forEach function, the script uploads each file into a separate folder (with the same name).
I would like to upload each file of one form submission into one folder.
HTML:
<iframe name="hidden_iframe" id="hidden_iframe" style="display:none;" onload="if(submitted) { picUploadJs(myForm); }"></iframe>
<form id="myForm" class="col s12" action="https://docs.google.com/forms/d/e/xxx/formResponse" target="hidden_iframe" onsubmit="submitted=true;">
<input placeholder="1234" name="entry.1234" id="user" type="text">
<label for="user">User:</label>
<input name="picToLoad" type="file" id="sampleFile" />
<div id="status" style="display: none">
Uploading. Please wait...
</div>
<button type="submit" name="action">Send</button> <!-- Modified -->
</form>
Javascript:
function picUploadJs(myForm) {
const f = document.getElementById("files");
[...f.files].forEach((file, i) => {
const fr = new FileReader();
fr.onload = (e) => {
const data = e.target.result.split(",");
const obj = {fileName: file.name, mimeType: data[0].match(/:(\w.+);/)[1], data: data[1]};
document.getElementById('status').style.display = 'inline';
google.script.run.withSuccessHandler(updateOutput).processForm(obj);
}
fr.readAsDataURL(file);
});
}
function updateOutput() {
var outputDiv = document.getElementById('status');
outputDiv.innerHTML = "The File was UPLOADED!";
window.location='https://Thankyou';
}
Google Script:
function doGet(e) {
Logger.log("doGet done");
return HtmlService.createTemplateFromFile('test')
.evaluate() // evaluate MUST come before setting the Sandbox mode
.setTitle('Form')
//.setSandboxMode();//Defaults to IFRAME which is now the only mode available
}
function processForm(theForm) {
var dateTime = Utilities.formatDate(new Date(), "GMT+2", "dd-MM-yy_HH-mm");
var email = Session.getActiveUser().getEmail();
var parentFolder=DriveApp.getFolderById('xxxxx');
var newFolder=parentFolder.createFolder(email + "_" + dateTime);
var newFolderId = DriveApp.getFoldersByName(newFolder).next().getId();
var fileBlob = Utilities.newBlob(Utilities.base64Decode(theForm.data), theForm.mimeType, theForm.fileName);
var fldrSssn = DriveApp.getFolderById(newFolderId);
fldrSssn.createFile(fileBlob);
return true;
}
When the files are submitted, you want to create one new folder every submitting.
You want to upload multiple files to the created folder.
If my understanding is correct, how about this modification? Please think of this as just one of several answers.
Modified scripts:
HTML:
Please modify as follows.
From:
<input name="picToLoad" type="file" id="sampleFile" />
To:
<input name="picToLoad" type="file" id="files" multiple />
Javascript:
Please modify picUploadJs().
function picUploadJs(myForm) {
const f = document.getElementById("files");
google.script.run.withSuccessHandler((folderId) => { // Added
var files = [...f.files];
files.forEach((file, i) => {
const fr = new FileReader();
fr.onload = (e) => {
const data = e.target.result.split(",");
const obj = {fileName: file.name, mimeType: data[0].match(/:(\w.+);/)[1], data: data[1]};
document.getElementById('status').style.display = 'inline';
google.script.run.withSuccessHandler(() => {
if (i == files.length - 1) updateOutput();
}).processForm(obj, folderId); // Modified
}
fr.readAsDataURL(file);
});
}).createFolder();
}
Google Apps Script:
I separated processForm() to processForm() and createFolder().
// Added
function createFolder() {
var dateTime = Utilities.formatDate(new Date(), "GMT+2", "dd-MM-yy_HH-mm");
var email = Session.getActiveUser().getEmail();
var parentFolder = DriveApp.getFolderById('xxxxx');
var newFolder = parentFolder.createFolder(email + "_" + dateTime);
var newFolderId = DriveApp.getFoldersByName(newFolder).next().getId();
return newFolderId;
}
// Modified
function processForm(theForm, newFolderId) {
var fileBlob = Utilities.newBlob(Utilities.base64Decode(theForm.data), theForm.mimeType, theForm.fileName);
var fldrSssn = DriveApp.getFolderById(newFolderId);
fldrSssn.createFile(fileBlob);
return true;
}
Note:
In your HTML, the top line is as follows.
<form id="myForm" target="hidden_iframe" onsubmit="submitted=true;">
From your previous question, if you are using the following lines, please update your question. I supposed that you are using the following script.
<iframe name="hidden_iframe" id="hidden_iframe" style="display:none;" onload="if(submitted) { picUploadJs(myForm); }"></iframe>
<form id="myForm" action="https://docs.google.com/forms/d/e/xxx/formResponse" target="hidden_iframe" onsubmit="submitted=true;">
If this was not the result you want, I apologize.
Related
Here is my code:
<body>
<input type="file" id="invisible_file_input" name="files[]" style="display:none">
<div class="button_panel">
<span class="button" onclick="sel_local_images()">Select Files</span>
</div>
</body>
<script>
var invisible_file_input = document.getElementById("invisible_file_input");
var file_path = "C:\\Users\\a142\\Desktop\\data"
function sel_local_images() {
invisible_file_input.setAttribute('multiple', 'multiple');
invisible_file_input.accept = '.jpg,.jpeg,.png,.bmp';
invisible_file_input.onchange = project_file_add_local;
invisible_file_input.click();
}
function project_file_add_local(event) {
var user_selected_images = event.target.files;
console.log("MAIN FILE = ", user_selected_images);
for ( var i = 0; i < user_selected_images.length; ++i ) {
console.log("user_selected_images", user_selected_images[i]);
}
}
</script>
Output:
What above code does is it makes button Select Files and after selecting multiple files console.log gives me above output.
But what I want is instead of clicking button I just have to specify file_path variable. and it gives me same result above. So that whenever i open html file file_path gets executed and i get same output as what event.target.files is giving here but without Manually select files from button.
This is a second part to the problem that had been resolved here: Insert Image Link from Google Drive into Google Sheets After Uploading an Image via Web App
I'm developing a web application where a user can upload a picture by clicking on a button. This action will upload pictures into a certain directory in my google drive with a unique folder and name.
Now, I'm trying to copy and paste the google drive link of a picture any time it has been uploaded.
With the help of #Tanaike, I was able to get the link of the url from google drive into the google sheet when I pre-assign a part of the folder name (fn) and the picture title (i) within the getFileUrl(fn,i) function in "Code.gs". But I get this "TypeError: Cannot call method "getFilesByName" of undefined." output whenever I try to pass the user-input "fn" and "i".
page.html
--This is the front end, where a user uploads the picture
<html>
<head>
<body>
<form action="#" method="post" enctype="multipart/form-data">
<div class="row">
<div class="file-field input-field">
<div class="waves-effect waves-light btn-small">
<i class="material-icons right">insert_photo</i>
<span>Import Picture</span>
<input id="files" type="file" name="image">
</div>
<div class="file-path-wrapper">
<input disabled selected type="text" class="file-path
validate" placeholder="Choose an image">
</div>
</div>
</div>
</form>
<?!= include("page-js"); ?>
</div> <!-- CLOSE CONTAINER-->
</body>
</html>
This is part of the javascript to put relevant info in an array, which will later be used to append a row in the google sheet
page-js.html
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<script src="https://gumroad.com/js/gumroad.js"></script>
document.getElementById("files").addEventListener("loadend",doStuff1);
document.getElementById("addAnother").addEventListener("click",doStuff1);
<script>
function doStuff1(){
num.picNum2=i;
var personName=document.getElementById("fn").value;
google.script.run.withSuccessHandler(doStuff2).getFileUrl("fn","i"); // Modified by Tanaike
var userInfo ={};
userInfo.firstName= document.getElementById("fn").value;
userInfo.number=i;
userInfo.fileUrl=fileId00;
num.picNum=i;
i++;
google.script.run.userClicked(userInfo);
}
// Added by Tanaike
function doStuff2(fileId00) {
var userInfo = {};
userInfo.firstName = document.getElementById("fn").value;
userInfo.number = i;
userInfo.fileUrl = "https://docs.google.com/document/d/"+fileId00 +"/";
i++;
google.script.run.userClicked(userInfo);
}
</script>
This is part of the javascript to upload picture file into the Google drive
(still part of page-js.html)
var file,
reader = new FileReader();
var today = new Date();
var date = today.getFullYear()+'-'+(today.getMonth()+1)+'- '+today.getDate();
reader.onloadend = function(e) {
if (e.target.error != null) {
showError("File " + file.name + " could not be read.");
return;
} else {
google.script.run
.withSuccessHandler(showSuccess)
.uploadFileToGoogleDrive(e.target.result,num.picNum,date,$('input#fn')
.val(),$('input#date').val());
}
};
function showSuccess(e) {
if (e === "OK") {
$('#forminner').hide();
$('#success').show();
} else {
showError(e);
}
}
function submitForm() {
var files = $('#files')[0].files;
if (files.length === 0) {
showError("Please select a image to upload");
return;
}
file = files[0];
if (file.size > 1024 * 1024 * 5) {
showError("The file size should be < 5 MB.");
return;
}
showMessage("Uploading file..");
reader.readAsDataURL(file);
}
function showError(e) {
$('#progress').addClass('red-text').html(e);
}
function showMessage(e) {
$('#progress').removeClass('red-text').html(e);
}
</script>
This part grabs the array "userInfo" and appends the content in a row within a designated google sheet. Any time, I click on the button in the front end, it creates a new row.
This is where if I set fn and i values within the getFileUrl function manually and have a correponding picture and a folder under the designated directory, I get a valid link back in my google sheet. However, if I leave the argument as variables that the user input in the web app, I get the aforementioned error in my link within the sheet.
Code.gs
//google sheet web script
var url="https://docs.google.com/spreadsheets/d/XXXXX";
function getFileUrl(fn,i){
try{
var today0 = new Date();
var date0 = today0.getFullYear()+'-'+(today0.getMonth()+1)+'-'
+today0.getDate();
var dropbox0 = "OE Audit Pictures";
var folder0,folders0 = DriveApp.getFoldersByName(dropbox0);
while (folders0.hasNext())
var folder0=folders0.next();
var dropbox20=[date0,fn].join(" ");
var folder20,folders20=folder0.getFoldersByName(dropbox20);
while (folders20.hasNext())
var folder20=folders20.next();
var file0, files0= folder20.getFilesByName(i);
while (files0.hasNext())
var file0=files0.next();
var fileId0=file0.getUrl();
return fileId0;
} catch(f){
return f.toString();
}
}
function userClicked(userInfo){
var ss= SpreadsheetApp.openByUrl(url);
var ws=ss.getSheetByName("Data");
ws.appendRow([userInfo.number,new Date(),
userInfo.firstName,userInfo.fileUrl]);
}
function include(filename){
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
function uploadFileToGoogleDrive(data, file, fn, date) {
try {
var dropbox = "OE Audit Pictures";
var folder, folders = DriveApp.getFoldersByName(dropbox);
if (folders.hasNext()) {
folder = folders.next();
} else {
folder = DriveApp.createFolder(dropbox);
}
var contentType = data.substring(5,data.indexOf(';')),
bytes =
Utilities.base64Decode(data.substr(data.indexOf('base64,')+7)),
blob=Utilities.newBlob(bytes, contentType, file)
var dropbox2=[fn,date].join(" ");
var folder2, folders2=folder.getFoldersByName(dropbox2)
if (folders2.hasNext()){
folder2=folders2.next().createFile(blob);
} else {
file = folder.createFolder([fn,date].join(" ")).createFile(blob);
}
return "OK";
} catch (f) {
return f.toString();
}
}
As #Jescanellas and #Tanaike commented, the better approach is to fix your code editing the function doStuff2 in page-js.html as this:
function doStuff2(fileId00) {
var userInfo = {};
userInfo.firstName = document.getElementById("fn").value;
userInfo.number = i;
userInfo.fileUrl = "https://docs.google.com/document/d/"+fileId00 +"/";
i++;
google.script.run.userClicked(userInfo);
}
About the error you're getting, you're not using the brackets in the whiles, this is causing the errors because only takes the first line after the while inside the loop. Code.gs:
while (folders0.hasNext()) {
var folder0=folders0.next();
var dropbox20=[date0,fn].join(" ");
var folder20,folders20=folder0.getFoldersByName(dropbox20);
while (folders20.hasNext()) {
var folder20=folders20.next();
var file0, files0= folder20.getFilesByName(i);
while (files0.hasNext()) {
var file0=files0.next();
var fileId0=file0.getUrl();
return fileId0;
}
}
}
Regarding your question about the file Id, you can get it easily after you create the file, because this will return you a File object from which you can get the ID using the getId method [1]:
file = folder.createFolder([fn,date].join(" ")).createFile(blob);
fileId = file.getId();
[1] https://developers.google.com/apps-script/reference/drive/file
I'm completely new to the subject of JSON and I was wondering how to parse JSON from an input value in my form.
I'm trying to string the inputs into an array like {"task" : "(input) ", "(input) "} {"description" : "(input ", "(input)"}
I tried to follow the same directions as this post: Adding a new array element to a JSON object but they're referring to strings already formulated when I want to be able to parse JSON the same way from an input in my form. I want to be able to save every input and add a new array element the same way.
Bottom code runs smoothly but I'm such a noobie at parsing JSON D: any help is appreciated.
function submitForm() {
var task = myForm.task.value;
var desc = myForm.description.value;
var FormData = {
task: task,
description: desc
};
myJSON = JSON.stringify(FormData);
localStorage.setItem("formJSON", myJSON);
text = localStorage.getItem("formJSON");
obj = JSON.parse(text);
addTask(task);
addDescription(desc);
console.log(FormData);
return false;
};
newArray = [task, description];
var taskArray = [];
var descriptionArray = [];
var task = document.getElementById("task").value;
var description = document.getElementById("description").value;
function addTask(task) {
taskArray.push(task);
console.log(
"Tasks: " + taskArray.join(", "));
}
function addDescription(description) {
descriptionArray.push(description);
console.log("Description: " + descriptionArray.join(", "));
};
<!DOCTYPE html>
<html>
<title>Task Form</title>
<body>
<form class="form-inline" name="myForm" onsubmit=" return submitForm()">
<label class="required">*Task and Description* </label>
<!first text box>
<div class="form-group">
<input type="text" id="task" placeholder="Task">
</div>
<!second comment box>
<div class="form-group">
<input type="text" id="description" placeholder="Description">
</div>
<button type="submit" class="btn btn-default submit">Submit</button>
</form>
<script type="text/javascript " src="json.js "></script>
</body>
</html>
You should be storing the array of all tasks in localStorage, not just a single task. When the user saves a new task, read the JSON from local storage, parse it, add the new task to the array, and save that.
function submitForm() {
var task = myForm.task.value;
var desc = myForm.description.value;
var FormData = {
task: task,
description: desc
};
var arrayJSON = localStorage.getItem("formJSON") || "[]";
var taskArray = JSON.parse(arrayJSON);
taskArray.push(FormData);
localStorage.setItem("formJSON", JSON.stringify(taskArray));
addTask(task);
addDescription(desc);
console.log(FormData);
return false;
};
How do I get the file from the form to save it to Parse?
I tried several dozen variations of this:
<form id="image_form" enctype="multipart/form-data" action="/upload/image" method="post">
<h3>Upload icon image: </h3>
<p><input id="image_file" name="image_file" type="file"></p>
<p><input id="event_submit" type="submit" value="Create" onclick="makeEventSnip();"></p>
</form>
and in the javascript:
function makeEventSnip() {
event.preventDefault();
var fileUploadControl = document.getElementById('image_file');
var file = fileUploadControl.files[0];
var name = "icon.png";
var iconImageFile = new Parse.File(name, file);
alert(iconImageFile.getUrl());
I tried to do it the way they say to in Parse's JS Guide (https://parse.com/docs/js/guide) but that doesn't work either. It doesn't appear to be grabbing anything from the form. What am I missing?
try this
var fileUploadControl = document.getElementById('image_file');
var file = fileUploadControl.files[0];
var name = "icon.png";
var iconImageFile = new Parse.File(name, file);
iconImageFile.save().then(function() {//you need to call after save the file
alert(iconImageFile._url);
}
It is possible to upload multiple files in onedrive(skydrive) using WL.upload ? I tried something but I always get an error like "element must be an html input element" or something like this. I use onedrive sdk 5.6 and the application is build in ASP.NET MVC 5. The problem is that I created an input of type="file" with the attribute multiple set so I can select multiple files from my computer but the upload method from WL api ask for an element property that is actual an id to an input element of type="file". Because my input is set on multiple I tried to iterate through the files that contains and to create an input element to pass to the method, but it's doesn't work because due to security reasons I can set a value of an input element.
So, does anybody knows how I can do this ? Thanks
This is what I have tried:
<div id="save-to-skydrive-dialog-content-multiple">
<p>select a file</p>
<form enctype='multipart/form-data' method='POST'>
<input id="save-to-skydrive-file-input-multiple" type="file" name="files[]" multiple />
</form>
<p>upload file</p>
<button id="save-to-skydrive-upload-multiple-button">upload multiple</button>
</div>
function saveMultipleToSkyDrive() {
WL.fileDialog({
mode: 'save'
}).then(function (response) {
var folder = response.data.folders[0];
var elements = document.getElementById("save-to-skydrive-file-input-multiple").files;
for (var i = 0; i < elements.length; i++) {
var htmlInPutElement = document.createElement('input');
htmlInPutElement.setAttribute('type', 'file');
htmlInPutElement.value = elements.item(i);
WL.api({
})
WL.upload({
path: folder.id,
element: htmlInPutElement,
overwrite: 'rename'
}).then(function (response) {
log("You save to" + response.source + ". " + "Below is the result of the upload");
log("");
log(JSON.stringify(response));
},
function (errorResponse) {
log("WL.upload errorResponse = " + JSON.stringify(errorResponse));
},
function (progress) {
});
}
}, function (errorResponse) {
log("WL.upload errorResponse = " + JSON.stringify(errorResponse));
}
);
Thanks.
From what I remember of messing with the input[file] element, you can't set the value of an input[file] like that, for security reasons.
var htmlInPutElement = document.createElement('input');
htmlInPutElement.setAttribute('type', 'file');
htmlInPutElement.value = elements.item(i);
A solution would be to post the files to an interim action on your controller, and then do the OneDrive API stuff in that action method instead. You can iterate through Request.Files (although it can be tricky with the multiple property, I learned the hard way - see this post for more info
I find the answer, but not in javascript, in c# code.
Html input:
<p>Upload Files</p>
<div id="save-to-skydrive-dialog-content">
#using (Html.BeginForm("UploadFiles", "Auth", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<input type="file" name="file" multiple />
<input type="submit" value="Upload File"/>
}
</div>
C# method:
[HttpPost]
public async Task<ActionResult> UploadFiles()
{
var files = Request.Files;
if (Request.Files.Count > 0)
{
LiveLoginResult loginStatus = await authClient.InitializeWebSessionAsync(HttpContext);
if (loginStatus.Status == LiveConnectSessionStatus.Connected)
{
connectedClient = new LiveConnectClient(this.authClient.Session);
LiveOperationResult result = await connectedClient.GetAsync("me/skydrive");
string folderId = (string)result.Result["id"];
for (var i = 0; i < Request.Files.Count; i++)
{
var fileName = Request.Files[i].FileName;
var fileStream = Request.Files[i].InputStream;
LiveOperationResult uploadResult = await connectedClient.UploadAsync(folderId, fileName, fileStream, OverwriteOption.Overwrite);
}
}
}
return View("~/Views/Home/Index.cshtml");
}