1

I know there are existing codes to build a form that can upload multiple files (bulk import) such as this Uploading Multiple Files to Google Drive with Google App Script, but I was just wondering if it's possible to do one that asks for bulk import (or big sized file) multiple times? The existing scripts seem to pass the id of one input in the upload function using document.getFileByID(), and I can't pass multiple id's in it.

Here are the codes that I was referring to (copied from Uploading Multiple Files to Google Drive with Google App Script):

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();
  }
}
<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>

Thanks for any advice!

Tanaike
  • 181,128
  • 11
  • 97
  • 165
  • I have to apologize for my poor English skill. Unfortunately, I couldn't understand `bulk import (or big sized file) multiple times` you expect. Can I ask you about the detail of it? And, can I ask you about the file size of `big sized file` you are expecting? – Tanaike Apr 11 '22 at 02:18
  • Sorry! I'm not a native English speaker myself. I was trying to design a form that allows people to upload pictures/videos (size >50mb). There are existing codes for this, but I also wanted to create two prompts for two different types of pictures/videos. For instance, one for videos shot at night, with info about aperture etc, and the other for videos shot during the day, with similar additional info – LauraEverdeen Apr 11 '22 at 02:35
  • Thank you for replying. About `There are existing codes for this`, can you add it to your question? And, I cannot understand `but I also wanted to create two prompts for two different types of pictures/videos.`. Do you want to have 2 file input tags and want to upload them to your Google Drive? But, in this case, I cannot understand `bulk import`. Can I ask you about the detail of your question? – Tanaike Apr 11 '22 at 02:44
  • Thanks for the advice. I have copied the codes into my question. As for bulk import, I just meant to have users import multiple files in one go. And yes, I want to have 2 file input tags, each allows the user to upload multiple files (size>50mb) and upload all the files attached to google drive. – LauraEverdeen Apr 11 '22 at 03:36
  • Thank you for replying. When I saw your added script, it seems that your showing script is the same with the script of [this](https://stackoverflow.com/q/31126181/7108653). In your question, you say `I want to have 2 file input tags`. And, I had thought that you wanted to upload 2 files using 2 input tags. But, your showing script includes other input tags except for the file input tag. So, can I ask you about the detail of your goal? – Tanaike Apr 11 '22 at 03:41
  • There is a file input tag in the HTML file. My goal is to create a form that asks for additional information about two different types of images/videos and then asks the users to upload the said media. My problem is that for the iteratorFileUpload() function, it is using document.getElementById('myFile').files to get the input from user, and I can't figure out how to get the function to go over multiple inputs as getElementById() can only accept one input ID. And, as I just realized, this code is using folder.createFile() in the script, so it might not accept file larger than 50mb? – LauraEverdeen Apr 11 '22 at 04:21
  • Thank you for replying. In your question, you want to upload only 2 files to your Google Drive using 2 input tags. The file size is more than 50 MB. Is my understanding correct? – Tanaike Apr 11 '22 at 05:17
  • Basically yes. I want to upload two sets of files using two input tags. Each input tag can take one or more files, but overall the file size will be larger than 50mb – LauraEverdeen Apr 11 '22 at 05:45
  • Thank you for replying. From your reply, I proposed an answer. Could you please confirm it? If that was not useful, I apologize. And, this sample script is a simple sample script. So, after you tested this, please modify it for your actual situation. – Tanaike Apr 11 '22 at 05:50

1 Answers1

3

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.

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.

enter image description here

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:

Tanaike
  • 181,128
  • 11
  • 97
  • 165