Uploading Multiple Files to Google Drive with Google App Script

Uploading Multiple Files to Google Drive with Google App Script

I know this question is old, but after finding it and related ones, I was never able to get the multiple file upload working. So after a lot of banging my head against walls, I wanted to post a full example (.html and .gs) in case anyone in the future is looking for one to get started. This is working when I deploy it right now and will hopefully work for other people out there. Note that I just hardcoded the folder in the drive to use in the code.gs file, but that can be easily changed.

form.html:

<body>
<div id="formcontainer">

<label for="myForm">Facilities Project Database Attachment Uploader:</label>

<br><br>

<form id="myForm">
<label for="myForm">Project Details:</label>
<div>
<input type="text" name="zone" placeholder="Zone:">
</div>
<div>
<input type="text" name="building" placeholder="Building(s):">
</div>
<div>
<input type="text" name="propertyAddress" placeholder="Property Address:">
</div>
<div>

<label for="fileText">Project Description:</label>

<TEXTAREA name="projectDescription"
placeholder="Describe your attachment(s) here:"
style ="width:400px; height:200px;"
></TEXTAREA>

</div>
<br>

<label for="attachType">Choose Attachment Type:</label>
<br>
<select name="attachType">
<option value="Pictures Only">Picture(s)</option>
<option value="Proposals Only">Proposal(s)</option>
<option value="Pictures & Proposals">All</option>
</select>
<br>

<label for="myFile">Upload Attachment(s):</label>
<br>

<input type="file" name="filename" id="myFile" multiple>

<input type="button" value="Submit" onclick="iteratorFileUpload()">

</form>
</div>

<div id="output"></div>
<div id="progressbar">
<div class="progress-label"></div>
</div>

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.css">
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>

<script>

var numUploads = {};
numUploads.done = 0;
numUploads.total = 0;

// Upload the files into a folder in drive
// This is set to send them all to one folder (specificed in the .gs file)
function iteratorFileUpload() {
var allFiles = document.getElementById('myFile').files;

if (allFiles.length == 0) {
alert('No file selected!');
} else {
//Show Progress Bar

numUploads.total = allFiles.length;
$('#progressbar').progressbar({
value : false
});//.append("<div class='caption'>37%</div>");
$(".progress-label").html('Preparing files for upload');
// Send each file at a time
for (var i = 0; i < allFiles.length; i++) {
console.log(i);
sendFileToDrive(allFiles[i]);
}
}
}

function sendFileToDrive(file) {
var reader = new FileReader();
reader.onload = function (e) {
var content = reader.result;
console.log('Sending ' + file.name);
var currFolder = 'Something';
google.script.run.withSuccessHandler(updateProgressbar).uploadFileToDrive(content, file.name, currFolder);
}
reader.readAsDataURL(file);
}

function updateProgressbar( idUpdate ){
console.log('Received: ' + idUpdate);
numUploads.done++;
var porc = Math.ceil((numUploads.done / numUploads.total)*100);
$("#progressbar").progressbar({value: porc });
$(".progress-label").text(numUploads.done +'/'+ numUploads.total);
if( numUploads.done == numUploads.total ){
//uploadsFinished();
numUploads.done = 0;
};
}
</script>

<script>
function fileUploaded(status) {
document.getElementById('myForm').style.display = 'none';
document.getElementById('output').innerHTML = status;
}

</script>

<style>
body {
max-width: 400px;
padding: 20px;
margin: auto;
}
input {
display: inline-block;
width: 100%;
padding: 5px 0px 5px 5px;
margin-bottom: 10px;
-webkit-box-sizing: border-box;
‌​ -moz-box-sizing: border-box;
box-sizing: border-box;
}
select {
margin: 5px 0px 15px 0px;
}
input[type="submit"] {
width: auto !important;
display: block !important;
}
input[type="file"] {
padding: 5px 0px 15px 0px !important;
}
#progressbar{
width: 100%;
text-align: center;
overflow: hidden;
position: relative;
vertical-align: middle;

}
.progress-label {
float: left;
margin-top: 5px;
font-weight: bold;
text-shadow: 1px 1px 0 #fff;
width: 100%;
height: 100%;
position: absolute;
vertical-align: middle;
}
</style>
</body>

code.gs:

function doGet() {
return HtmlService.createHtmlOutputFromFile('form')
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}

function uploadFileToDrive(base64Data, fileName) {
try{
var splitBase = base64Data.split(','),
type = splitBase[0].split(';')[0].replace('data:','');

var byteCharacters = Utilities.base64Decode(splitBase[1]);
var ss = Utilities.newBlob(byteCharacters, type);
ss.setName(fileName);

var dropbox = "Something"; // Folder Name
var folder, folders = DriveApp.getFoldersByName(dropbox);

if (folders.hasNext()) {
folder = folders.next();
} else {
folder = DriveApp.createFolder(dropbox);
}
var file = folder.createFile(ss);

return file.getName();
}catch(e){
return 'Error: ' + e.toString();
}
}

Multiple bulk import entries using Google Apps Script

I believe your goal is as follows.

  • You want to upload only 2 files to your Google Drive using 2 input tags. Each file size is more than 50 MB.
  • You want to achieve this using Web Apps. The HTML is put in the Google Apps Script project including the script of Web Apps.

Issue and workaround:

In this current stage, when the file size is more than 50 MB, the file content cannot be directly saved as a file using Google Apps Script, because of the current specification at Google side. SO, in this case, it is required to upload the file using the resumable upload with Drive API. Ref In this case, it is required to prepare a script for achieving the resumable upload. But, fortunately, I have already created for achieving this using Javascript library. In this answer, I would like to propose a sample script using this Javascript library.

Usage:

1. Create Google Apps Script for Web Apps.

In order to use Web Apps, please create a new Google Apps Script project.

2. Sample script.

Please copy and paste the following script to the script editor of the created Google Apps Script project.

Google Apps Script side: Code.gs

function main() {
var html = HtmlService.createHtmlOutputFromFile("index");
SpreadsheetApp.getUi().showSidebar(html);
}

function getAuth() {
// DriveApp.createFile() // This is used for adding the scope of "https://www.googleapis.com/auth/drive".
return ScriptApp.getOAuthToken();
}

HTML & Javascript side: index.html

<input type="file" id="file1" />
<input type="file" id="file2" />
<input type="button" onclick="run()" value="Upload" />
<div id="progress"></div>

<script src="https://cdn.jsdelivr.net/gh/tanaikech/ResumableUploadForGoogleDrive_js@master/resumableupload_js.min.js"></script>
<script>
function run() {
google.script.run.withSuccessHandler(accessToken => ResumableUploadForGoogleDrive(accessToken)).getAuth();
}

function ResumableUploadForGoogleDrive(accessToken) {
const f1 = document.getElementById("file1").files[0];
const f2 = document.getElementById("file2").files[0];
[f1, f2].forEach((file, i) => {
if (!file) return;
let fr = new FileReader();
fr.fileName = file.name;
fr.fileSize = file.size;
fr.fileType = file.type;
fr.readAsArrayBuffer(file);
fr.onload = e => {
var id = "p" + ++i;
var div = document.createElement("div");
div.id = id;
document.getElementById("progress").appendChild(div);
document.getElementById(id).innerHTML = "Initializing.";
const f = e.target;
const resource = { fileName: f.fileName, fileSize: f.fileSize, fileType: f.fileType, fileBuffer: f.result, accessToken: accessToken };
const ru = new ResumableUploadToGoogleDrive();
ru.Do(resource, function (res, err) {
if (err) {
console.log(err);
return;
}
console.log(res);
let msg = "";
if (res.status == "Uploading") {
msg = Math.round((res.progressNumber.current / res.progressNumber.end) * 100) + "% (" + f.fileName + ")";
} else {
msg = res.status + " (" + f.fileName + ")";
}
// if (res.status == "Done") google.script.run.putFileInf(res.result);
document.getElementById(id).innerText = msg;
});
};
});
}
</script>
  • When you want to put the uploaded files to the specific folder, please modify const resource = { fileName: f.fileName, fileSize: f.fileSize, fileType: f.fileType, fileBuffer: f.result, accessToken: accessToken }; as follows.

      const resource = { fileName: f.fileName, fileSize: f.fileSize, fileType: f.fileType, fileBuffer: f.result, accessToken: accessToken, folderId: "###folderId###" };

3. Enable Drive API.

Please enable Drive API at Advanced Google services.

4. Deploy Web Apps.

The detailed information can be seen at the official document.

  1. On the script editor, at the top right of the script editor, please click "click Deploy" -> "New deployment".
  2. Please click "Select type" -> "Web App".
  3. Please input the information about the Web App in the fields under "Deployment configuration".
  4. Please select "Me" for "Execute as".
    • This is the importance of this workaround.
  5. Please select "Anyone" for "Who has access".
    • For testing this script, I thought that this setting might be suitable.
  6. Please click "Deploy" button.
  7. Copy the URL of the Web App. It's like https://script.google.com/macros/s/###/exec.
    • When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful this.
    • You can see the detail of this in the report of "Redeploying Web Apps without Changing URL of Web Apps for new IDE".

5. Testing:

As a simple sample script, when the above HTML and Javascript are run on a sidebar, the following result is obtained. Of course, this result can be obtained using Web Apps. When this script is tested, you can see that the files can be uploaded with the asynchronous process. This demonstration is from here.

Sample Image

Note:

  • In this case, the access token is retrieved from Google Apps Script side using ScriptApp.getOAuthToken(). For testing this script, you can use the scope of https://www.googleapis.com/auth/drive. But, if you want to keep the security when you use this script in your actual situation, I would like to propose to use https://www.googleapis.com/auth/drive.file as the scope. By this, the access token can access only the files created by this client.

  • When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful this.

  • You can see the detail of this in the report of "Redeploying Web Apps without Changing URL of Web Apps for new IDE".

References:

  • Web Apps
  • Taking advantage of Web Apps with Google Apps Script
  • Resumable Upload of Multiple Files with Asynchronous Process for Google Drive

Upload Local Files from multiple upload element in Google Drive using Google Apps Script

I believe your goal as follows.

  • You want to upload the multiple files and zip the files as one zip file using Google Apps Script.

In your script, how about the following modification? I think that fileList.files[i].name is required to be modified to file.name. Ref

From:

return r({fileName: fileList.files[i].name, mimeType: data[0].match(/:(\w.+);/)[1], data: data[1]});

To:

return r({fileName: file.name, mimeType: data[0].match(/:(\w.+);/)[1], data: data[1]});

Note:

  • At Utilities.zip, when the files with the same filenames are used, an error occurs. Please be careful this.
  • And, in the current stage, the maximum size of Blob for creating a file of Google Apps Script is 50 MB. Please be careful this.

References:

  • Related thread.
    • How to get file(s) from an HTML file input and send it as an attachment to an email in Google Apps Script?

Multiple Upload in Google Script

Based from this thread, as of right now you have to use a work around to work with multiple files. The multiple attribute only works in IFRAME mode, but file inputs are broken in IFRAME mode.

Here's a sample code for multi-file uploading. Limitations are files must be under 10 MB.

function doGet() {
return HtmlService.createHtmlOutputFromFile('index').setSandboxMode(HtmlService.SandboxMode.IFRAME);

}

function saveFile(data,name,folderId) {

var contentType = data.substring(5,data.indexOf(';'));
var file = Utilities.newBlob(Utilities.base64Decode(data.substr(data.indexOf('base64,')+7)), contentType, name);
DriveApp.getFolderById(folderId).createFile(file);

}

Additional references:

  • Uploading Multiple Files to Google Drive with Google App Script
  • Multiple File Upload to Google Drive Using Google Script

Upload files to Google Drive using Google Apps Script

I think that your script works when both the inputted URL and the folder ID is correct in your script. From this situation, please check the following point.

  • At the Web Apps of Google Apps script, when you use the URL of https://script.google.com/macros/s/###/exec, when the script of Web Apps is modified, the latest script is reflected to the Web Apps by redeploying the Web Apps as new version.

This is the important point for using the Web Apps of Google Apps Script. So I would like to propose to confirm above.

References:

  • Web Apps
  • Taking advantage of Web Apps with Google Apps Script

Upload file to my google drive with Google Apps Script (NO FORM IN GOOGLE)

I believe your goal as follows.

  • Your web site is not related to Google account. It's independent.
  • Your web site has a form for uploading a file.
  • When users submit the form, you want to upload the file to your Google Drive without the authorization, and want to return the URL of the uploaded file on Google Drive.
  • About "Database", this is your database. You will put the retrieved URL of the file to "Database" at the client side.

In this case, I think that your goal can be achieved using the Web Apps created by Google Apps Script.

Usage:

Please do the following flow.

1. Create new project of Google Apps Script.

Sample script of Web Apps is a Google Apps Script. So please create a project of Google Apps Script.

If you want to directly create it, please access to https://script.new/. In this case, if you are not logged in Google, the log in screen is opened. So please log in to Google. By this, the script editor of Google Apps Script is opened.

2. Prepare script.

Please copy and paste the following script (Google Apps Script) to the script editor. This script is for the Web Apps.

Server side: Google Apps Script

Please set the folder ID that you want to put the file.

function doPost(e) {
const folderId = "root"; // Or Folder ID which is used for putting the file instead of "root", if you need.

const blob = Utilities.newBlob(JSON.parse(e.postData.contents), e.parameter.mimeType, e.parameter.filename);
const file = DriveApp.getFolderById(folderId).createFile(blob);
const responseObj = {filename: file.getName(), fileId: file.getId(), fileUrl: file.getUrl()};
return ContentService.createTextOutput(JSON.stringify(responseObj)).setMimeType(ContentService.MimeType.JSON);
}

3. Deploy Web Apps.

  1. On the script editor, Open a dialog box by "Publish" -> "Deploy as web app".
  2. Select "Me" for "Execute the app as:".
    • By this, the script is run as the owner.
  3. Select "Anyone, even anonymous" for "Who has access to the app:".
  4. Click "Deploy" button as new "Project version".
  5. Automatically open a dialog box of "Authorization required".
    1. Click "Review Permissions".
    2. Select own account.
    3. Click "Advanced" at "This app isn't verified".
    4. Click "Go to ### project name ###(unsafe)"
    5. Click "Allow" button.
  6. Click "OK".
  7. Copy the URL of Web Apps. It's like https://script.google.com/macros/s/###/exec.
    • When you modified the Google Apps Script, please redeploy as new version. By this, the modified script is reflected to Web Apps. Please be careful this.

4. Upload a file from client side to server side.

Client side: HTML & Javascript

Please set the URL of your Web Apps to the following script.

<form id="form">
<input name="file" id="uploadfile" type="file">
<input name="filename" id="filename" type="text">
<input id="submit" type="submit">
</form>
<script>
const form = document.getElementById('form');
form.addEventListener('submit', e => {
e.preventDefault();
const file = form.file.files[0];
const fr = new FileReader();
fr.readAsArrayBuffer(file);
fr.onload = f => {

const url = "https://script.google.com/macros/s/###/exec"; // <--- Please set the URL of Web Apps.

const qs = new URLSearchParams({filename: form.filename.value || file.name, mimeType: file.type});
fetch(`${url}?${qs}`, {method: "POST", body: JSON.stringify([...new Int8Array(f.target.result)])})
.then(res => res.json())
.then(e => console.log(e)) // <--- You can retrieve the returned value here.
.catch(err => console.log(err));
}
});
</script>
  • At the client side, when you selected a file from your local PC and push the button, the file is uploaded to your Google Drive by retrieving the data at the Web Apps (server side).

Result:

When above script is run, the following value is returned. From this, you can retrieve the URL of the file.

{
"filename": "### inputted filename ###",
"fileId": "###",
"fileUrl": "https://drive.google.com/file/d/###/view?usp=drivesdk"
}

Note:

  • When you modified the script of Web Apps, please redeploy the Web Apps as new version. By this, the latest script is reflected to Web Apps. Please be careful this.
  • In above script, the maximum file size is 50 MB. Because in the current stage, the maximum blob size is 50 MB at Google Apps Script.

References:

  • Web Apps
  • Taking advantage of Web Apps with Google Apps Script

GAS how upload multiple file in google drive

Modification points:

  • In your script, for(var i=0; i<url.length; i++){}else{} is used. I thought that you might misunderstand the if statement and for loop.
  • When you want to compare the value at the if statement, please modify if(rc=200){ to if(rc==200){.
  • The response value from UrlFetchApp.fetchAll(url) is an array.
    • I think that the reason of the error message is this.
  • folder.createFile(fileBlob[i]) is folder.createFile(fileBlob).
  • file_img.getUrl()[i] is file_img.getUrl().
  • When file_img.setTrashed(true) is used, the downloaded files are moved to the trashbox. If you don't want to move them to the trashbox, please remove the line.
  • I think that when the values are put to the Spreadsheet by one request, the process cost will be low. In your script, I would like to propose to use setValues instead of appendRow.

When above points are reflected to your script, it becomes as follows.

Modified script:

Please copy and paste the following modified script. And please set the variable of sheet, and the folder ID.

function myFunction() {
// var sheet = SpreadsheetApp.getActiveSheet();

var url = ['http://www.pngall.com/wp-content/uploads/2/1-Number-PNG-Picture.png', 'https://www.yourcloudworks.com/wp-content/uploads/2019/09/number-digit-2-png-transparent-images-transparent-backgrounds-Number-2-PNG-images-free-download_PNG14949.png'];
var requests = url.map(u => ({url: u, muteHttpExceptions: true}));
var response = UrlFetchApp.fetchAll(requests);
var imgs = [];
for (var i = 0; i < response.length; i++) {
if (response[i].getResponseCode() == 200) {
var fileBlob = response[i].getBlob();
var folder = DriveApp.getFolderById("xxxxxx");
if (folder != null) {
var file_img = folder.createFile(fileBlob);
imgs.push([file_img.getUrl()]);
// file_img.setTrashed(true); // When this script is used, the downloaded files are moved to the trashbox.
}
}
}
if (imgs.length > 0) {
sheet.getRange(sheet.getLastRow() + 1, 1, imgs.length).setValues(imgs);
}
}
  • When muteHttpExceptions: true is used, the script can be run even when an error occurs.

References:

  • fetchAll(requests)
  • if...else
  • Loops and iteration


Related Topics



Leave a reply



Submit