简体   繁体   中英

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

so basically the task is quite simple, but I didn't find any workable solution for my problem. I have a huge upload script on my website (at the moment localhost), but lets reduce all the complexity to the only neccessary.

So I just want to upload a single file to Google Drive with Google App Script and receive the URL of it to save it in a var, to work with that information on a later point in my function.

Now the problem is I already have the form on my website, I dont want the form inside script.google.com as extra html, I want to transfer my user input to Google App Script, then upload it to google drive and return the url back to my website where I can save it into a var.

My problem now is, I cant put all the stuff together.

This is the form on my website (simplified):

<form name="myForm" method="post">
            <!-- <form name="first-form"> -->

  <input type="text" placeholder="Name" id="myName">
  <input type="file" name="myFile" id="myFile">
  <button onclick="UploadFile()" type="submit">submit</button>

</form>

So how can I upload my informations inside google drive and get back a result? How can I push the data in Google App Script without using iFrame or anything else?

THANK YOU!

**** Working example if html is in scripts.google.com ****

gs

function doGet(e) {
  return HtmlService.createHtmlOutputFromFile('forms.html').setTitle("Google File Upload by CTRLQ.org");
}


function uploadFileToGoogleDrive(data, file, name, email) {
  
  try {
    
    var dropbox = "Received Files";
    var folder, folders = DriveApp.getFoldersByName(dropbox);
    
    if (folders.hasNext()) {
      folder = folders.next();
    } else {
      folder = DriveApp.createFolder(dropbox);
    }
    
    /* Credit: www.labnol.org/awesome */
    
    var contentType = data.substring(5,data.indexOf(';')),
        bytes = Utilities.base64Decode(data.substr(data.indexOf('base64,')+7)),
        blob = Utilities.newBlob(bytes, contentType, file),
        file = folder.createFolder([name, email].join(" ")).createFile(blob);
    
    return "OK";
    
  } catch (f) {
    return f.toString();
  }
  
}

html in apps.googlescript

<!DOCTYPE html>
<html>
  <head>
    <base target="_blank">
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Google File Upload by CTRLQ.org</title>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.5/css/materialize.min.css">
    <style>
      .disclaimer{width: 480px; color:#646464;margin:20px auto;padding:0 16px;text-align:center;font:400 12px Roboto,Helvetica,Arial,sans-serif}.disclaimer a{color:#009688}#credit{display:none}
    </style>
  </head>
  <body>

    <!-- Written by Amit Agarwal amit@labnol.org --> 

    <form class="main" id="form" novalidate="novalidate" style="max-width: 480px;margin: 40px auto;">
      <div id="forminner">
        <div class="row">
          <div class="col s12">
            <h5 class="center-align teal-text">Upload Files to my Google Drive</h5>
            <p class="disclaimer">This <a href="http://www.labnol.org/internet/file-upload-google-forms/29170/">File Upload Form</a> (<a href="https://youtu.be/C_YBBupebvE">tutorial</a>) is powered by <a href="https://ctrlq.org/code/19747-google-forms-upload-files" target="_blank">Google Scripts</a></p>
          </div>
        </div>
        <div class="row">
          <div class="input-field col s12">
            <input id="name" type="text" name="Name" class="validate" required="" aria-required="true">
            <label for="name">Name</label>
          </div>
        </div>
        <div class="row">
          <div class="input-field col s12">
            <input id="email" type="email" name="Email" class="validate" required="" aria-required="true">
            <label for="email">Email Address</label>
          </div>
        </div>

        <div class="row">
          <div class="file-field input-field col s12">
            <div class="btn">
              <span>File</span>
              <input id="files" type="file">
            </div>
            <div class="file-path-wrapper">
              <input class="file-path validate" type="text" placeholder="Select a file on your computer">
            </div>
          </div>
        </div>

        <div class="row">
          <div class="input-field col s6">
            <button class="waves-effect waves-light btn submit-btn" type="submit" onclick="submitForm(); return false;">Submit</button>
          </div>   
        </div>
        <div class="row">
          <div class="input-field col s12" id = "progress">
          </div>
        </div>
      </div>
      <div id="success" style="display:none">
        <h5 class="left-align teal-text">File Uploaded</h5>
        <p>Your file has been successfully uploaded.</p>
        <p>The <a href="http://www.labnol.org/internet/file-upload-google-forms/29170/">pro version</a> (see <a href="">demo form</a>) includes a visual drag-n-drop form builder, CAPTCHAs, the form responses are saved in a Google Spreadsheet and respondents can upload multiple files of any size.</p>    
        <p class="center-align"><a  class="btn btn-large" href="https://gum.co/GA14?wanted=true" target="_blank">Upgrade to Pro</a></p>
      </div>
    </form>

    <div class="fixed-action-btn horizontal" style="bottom: 45px; right: 24px;">
      <a class="btn-floating btn-large red">
        <i class="large material-icons">menu</i>
      </a>
      <ul>
        <li><a class="btn-floating red"  href="https://gum.co/GA14" target="_blank" title="Buy License - File Upload Form"><i class="material-icons">monetization_on</i></a></li>
        <li><a class="btn-floating blue"  href="https://youtu.be/C_YBBupebvE" target="_blank" title="Video Tutorial"><i class="material-icons">video_library</i></a></li>
        <li><a class="btn-floating green" href="http://www.labnol.org/internet/file-upload-google-forms/29170/" target="_blank" title="How to Create File Upload Forms"><i class="material-icons">help</i></a></li>
      </ul>
    </div>

    <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/0.97.5/js/materialize.min.js"></script>
    <script src="https://gumroad.com/js/gumroad.js"></script>

    <script>

      var file, 
          reader = new FileReader();

      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, file.name, $('input#name').val(), $('input#email').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 file to upload");
          return;
        }

        file = files[0];

        if (file.size > 1024 * 1024 * 5) {
          showError("The file size should be < 5 MB. Please <a href='http://www.labnol.org/internet/file-upload-google-forms/29170/' target='_blank'>upgrade to premium</a> for receiving larger files in Google Drive");
          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>

  </body>

</html>

As reccomended Im going to describe the process here.

So we are on the website: www.example.com , there is a form with text input field and file field. Lets say we put in an image and call it example. Now if we press submit, I want to upload the image to google drive without any oAuth (Thats why we need to use google app script here) and name it to what we typed in the textfield. When the upload is done, I want the url of the image of google drive to be returned back to the website, so the form can continue working with the information. I want to save the returned url in a var then, to later on save it in a database. Thats why I need the result back to my website.

So scheme looks like the following:

Enter informations to form on website -> Redirected to google app script: take informations of website form field and upload file to google drive and name it like text input entry -> taking url of google drive as final result -> redirecting final url result back to website -> saving url result in var and continuing doing stuff from function on website -> at the end saving the informations from var to a database -> finish

------------------------------------------------ EDIT: ------------------

Thanks to @Tanaike Im a lot closer to the goal of my challenge here, so in order to see where I got stuck, I'm replicating my issue now:

I took the form with the script from your example:

<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>

and for google script:

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

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

Now when i tried to upload something I had following error: CORS Policy not able to fetch. So I changed this part to the following and added mode no cors:

const qs = new URLSearchParams({filename: form.filename.value || file.name, mimeType: file.type});
        fetch(`${url}?${qs}`, {method: "POST", mode: "no-cors", body: JSON.stringify([...new Int8Array(f.target.result)])})

This worked. Second try uploading the file caused in the following error: it says: syntax error: unexpected end of input

So I changed this line and removed the brackets from res.json

JSON.stringify([...new Int8Array(f.target.result)])})
        .then(res => res.json)

third try to upload the file actually worked with the following console result:

ƒ json() { [native code] }

But there is no file uploaded in google drive. Im missing something somewhere. Maybe we should create a folder and place the files in there.

Oh and another information: when i run doPost function in google app sript it says:

TypeError: Cannot read property 'postData' of undefined (line 13

EDIT2 -----------------------------------------

I addedhttps://drive.google.com/uc?export=download&id=###fileId### to your code and everything works fine. The file is getting uploaded.

Lets say we upload the file test.mp3 and we call it testdata. This is what we recieve:

{
  "filename": "testdata",
  "fileId": "###some id##",
  "fileUrl": "https://drive.google.com/uc?export=download&id=###fileId###"
}

Now when I open the file url, the browser downloads file but its called: testdata, not testdata.mp3. The filetyp ending is missing.

Second task: If you click the link, I want to open the file in browser, when its mp3 file for example i want that you can play the sound in webview, like it is here: https://files.freemusicarchive.org/storage-freemusicarchive-org/music/Creative_Commons/Dead_Combo/CC_Affiliates_Mixtape_1/Dead_Combo_-_01_-_Povo_Que_Cas_Descalo.mp3

I hope you can guide me!

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:

Lots of useful tips in https://stackoverflow.com/a/63391363/1585523 answer. Thank you for sharing, Instead of POSTing the file, we could also use

On the client: index.html

google.script.run.withSuccessHandler(fileName => {}).withFailureHandler(error => {}).saveInDrive(fileAsByteArray);

On the server: Code.gs

function saveInDrive(f) {
  const blob = Utilities.newBlob(f, "image/jpeg", "some-name");
  const file = DriveApp.getFolderById("root").createFile(blob);
  return file.getName()
}

You can even exchange complicated types like JS objects with binary info as values as well in this approach.

I do not think that adding

mode: 'no-cors'

is right way because then this mode will block app from reading response(cors policies), which by the way comes not from response to initial request but from second redirected GET request

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM