检查用户是否已运行

时间:2018-03-02 13:13:12

标签: google-apps-script

我运行Google Apps脚本,将文件上传到用户的Google云端硬盘文件中:

function doGet(e) {
  var blob = UrlFetchApp.fetch(e.parameters.url).getBlob();
  DriveApp.createFile(blob);
  return HtmlService.createHtmlOutput("DONE!");
}

我的网站加载了一个弹出窗口,该窗口使用该代码运行Google Apps脚本。工作正常。

现在,如何将返回与用户成功上传文件的网站进行通信?如同,如何与用户进行通信回复用户已经运行doGet()?`

某些类型的response处理必须存在吗?

完整的工作代码(在 JSBin 上测试):

<!DOCTYPE html>
<html>
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.js"></script>
  </head>
  <body>
    <div class="google-upload" data-url="https://calibre-ebook.com/downloads/demos/demo.docx">
      <span style="background-color: #ddd">Upload</span>
    </div>
    <script>
      $(function() {
        $(".google-upload").click(function() {
          var url = "https://script.google.com/macros/s/AKfycbwsuIcO5R86Xgv4E1k1ZtgtfKaENaKq2ZfsLGWZ4aqR0d9WBYc/exec"; // Please input the URL here.
          var withQuery = url + "?url=";
          window.open(withQuery + $('.google-upload').attr("data-url"), "_blank", "width=600,height=600,scrollbars=1");
        });
      });
    </script>
  </body>
</html>

因此,为了澄清,我想找出一种方法来确定用户是否已成功上传文件。类似的东西:

request.execute(function(response) {
    if (response.code == 'uploaded') {
        // uploaded, do stuff
    } else {
        // you get the idea...
    }
});

为此提供完整解决方案的赏金。

1 个答案:

答案 0 :(得分:7)

您可以使用jQuery的HtmlService方法传递数据,而不是返回$.getJSON对象,并使用doGetContentService函数中检索数据。 Google Apps脚本不接受CORS,因此使用JSONP是从脚本获取数据的最佳方式。 See this post了解更多信息。

<强> Working CodePen Example

为了清晰起见,我拆分了HTML和脚本。没有HTML从您的原始示例中更改。

<强> Code.gs

function doGet(e) { 
  var returnValue;

  // Set the callback param. See https://stackoverflow.com/questions/29525860/
  var callback = e.parameter.callback;

  // Get the file and create it in Drive
  try {
    var blob = UrlFetchApp.fetch(e.parameters.url).getBlob();
    DriveApp.createFile(blob);

    // If successful, return okay 
    // Structure this JSON however you want. Parsing happens on the client side.
    returnValue = {status: 'okay'};
  } catch(e) {
    Logger.log(e);
    // If a failure, return error message to the client
    returnValue = {status: e.message}
  }

  // Returning as JSONP allows for crossorigin requests
  return ContentService.createTextOutput(callback +'(' + JSON.stringify(returnValue) + ')').setMimeType(ContentService.MimeType.JAVASCRIPT);
}

客户端JS

$(function() {
  $(".google-upload").click(function() {
    var appUrl = "https://script.google.com/macros/s/AKfycbyUvgKdhubzlpYmO3Marv7iFOZwJNJZaZrFTXCksxtl2kqW7vg/exec"; 
    var query = appUrl + "?url=";
    var popupUrl = query + $('.google-upload').attr("data-url") + "&callback=?";

    console.log(popupUrl)

    // Open this to start authentication.
    // If already authenticated, the window will close on its own.
    var popup = window.open(popupUrl, "_blank", "width=600,height=600,scrollbars=1");

    $.getJSON(popupUrl, function(returnValue) {
      // Log the value from the script
      console.log(returnValue.status);
      if(returnValue.status == "okay") {
        // Do stuff, like notify the user, close the window
        popup.close();
        $("#result").html("Document successfully uploaded");
      } else {
        $("#result").html(returnValue);
      }
    })
  });
});

您可以通过在data-url参数中传递空字符串来测试错误消息。该消息将在控制台以及用户的页面中返回。

编辑3.7.18

上述解决方案存在控制授权流程的问题。在与驱动工程师(see thread here)进行研究和交流之后,我已将其重新设计为基于Apps Script API的自托管示例,并将项目作为API可执行文件而不是Apps脚本Web运行应用。这将允许您访问Apps脚本Web应用程序外的[run](https://developers.google.com/apps-script/api/reference/rest/v1/scripts/run)方法。

设置

关注Google Apps Script API instructions for JavaScript。 Apps脚本项目应该是独立的(不链接到文档)并作为API可执行文件发布。您需要打开云控制台并创建OAuth凭据和API密钥。

说明是否在计算机上使用Python服务器。我使用Node JS服务器http-server,但您也可以在线直播并从那里进行测试。您需要在云端控制台中将源列入白名单。

客户

由于这是自托管的,因此您需要一个纯HTML页面,通过JavaScript通过OAuth2 API授权用户。这是首选,因为它保持用户登录,允许对脚本进行多次API调用而无需重新授权。以下代码适用于此应用程序,并使用Google快速入门指南中的授权流程。

<强>的index.html

  <body>

    <!--Add buttons to initiate auth sequence and sign out-->
    <button id="authorize-button" style="display: none;">Authorize</button>
    <button id="signout-button" style="display: none;">Sign Out</button>

    <button onclick="uploadDoc()" style="margin: 10px;" id="google-upload" data-url="https://calibre-ebook.com/downloads/demos/demo.docx">Upload doc</button>

    <pre id="content"></pre>
</body>

<强> index.js

// Client ID and API key from the Developer Console
  var CLIENT_ID = 'YOUR_CLIENT_ID';
  var API_KEY = 'YOUR_API_KEY';
  var SCRIPT_ID = 'YOUR_SCRIPT_ID';

  // Array of API discovery doc URLs for APIs used by the quickstart
  var DISCOVERY_DOCS = ["https://script.googleapis.com/$discovery/rest?version=v1"];

  // Authorization scopes required by the API; multiple scopes can be
  // included, separated by spaces.
  var SCOPES = 'https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/script.external_request';

  var authorizeButton = document.getElementById('authorize-button');
  var signoutButton = document.getElementById('signout-button');
  var uploadButton = document.getElementById('google-upload');
  var docUrl = uploadButton.getAttribute('data-url').value;

  // Set the global variable for user authentication
  var isAuth = false;

  /**
   *  On load, called to load the auth2 library and API client library.
   */
  function handleClientLoad() {
    gapi.load('client:auth2', initClient);
  }

  /**
   *  Initializes the API client library and sets up sign-in state
   *  listeners.
   */
  function initClient() {
    gapi.client.init({
      apiKey: API_KEY,
      clientId: CLIENT_ID,
      discoveryDocs: DISCOVERY_DOCS,
      scope: SCOPES
    }).then(function () {
      // Listen for sign-in state changes.
    gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);

      // Handle the initial sign-in state.
      updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
      authorizeButton.onclick = handleAuthClick;
      signoutButton.onclick = handleSignoutClick;
      // uploadButton.onclick = uploadDoc;
    });
  }

  /**
   *  Called when the Upload button is clicked. Reset the
   *  global variable to `true` and upload the document. 
   *  Thanks to @JackBrown for the logic.
   */
  function updateSigninStatus(isSignedIn) {
    if (isSignedIn && !isAuth) {
      authorizeButton.style.display = 'none';
      signoutButton.style.display = 'block';
      uploadButton.style.display = 'block'
      uploadButton.onclick  = uploadDoc;
    } else if (isSignedIn && isAuth) {
      authorizeButton.style.display = 'none';
      signoutButton.style.display = 'block';
      uploadButton.style.display = 'block';
      uploadDoc();
    } else {
      authorizeButton.style.display = 'block';
      signoutButton.style.display = 'none';
      uploadButton.style.display = 'none';
      isAuth = false;
    }
  }

  /**
   *  Sign in the user upon button click.
   */
  function handleAuthClick(event) {
    gapi.auth2.getAuthInstance().signIn();
    isAuth = true;  // Update the global variable
  }


  /**
   *  Sign out the user upon button click.
   */
  function handleSignoutClick(event) {
    gapi.auth2.getAuthInstance().signOut();
    isAuth = false;  // update the global variable
  }

  /**
   * Append a pre element to the body containing the given message
   * as its text node. Used to display the results of the API call.
   *
   * @param {string} message Text to be placed in pre element.
   */
  function appendPre(message) {
    var pre = document.getElementById('content');
    var textContent = document.createTextNode(message + '\n');
    pre.appendChild(textContent);
  }

  /**
    * Handle the login if signed out, return a Promise
    * to call the upload Docs function after signin.
  **/
  function uploadDoc() {

      console.log("clicked!")
      var docUrl = document.getElementById('google-upload').getAttribute('data-url');

      gapi.client.script.scripts.run({
        'scriptId':SCRIPT_ID,
        'function':'uploadDoc',
        'parameters': [ docUrl ]
      }).then(function(resp) {
        var result = resp.result;
        if(result.error && result.error.status) {
          // Error before the script was Called
          appendPre('Error calling API');
          appendPre(JSON.parse(result, null, 2));
        } else if(result.error) {
          // The API executed, but the script returned an error.

            // Extract the first (and only) set of error details.
            // The values of this object are the script's 'errorMessage' and
            // 'errorType', and an array of stack trace elements.
            var error = result.error.details[0];
            appendPre('Script error message: ' + error.errorMessage);

            if (error.scriptStackTraceElements) {
              // There may not be a stacktrace if the script didn't start
              // executing.
              appendPre('Script error stacktrace:');
              for (var i = 0; i < error.scriptStackTraceElements.length; i++) {
                var trace = error.scriptStackTraceElements[i];
                appendPre('\t' + trace.function + ':' + trace.lineNumber);
              }
            }
          } else {
            // The structure of the result will depend upon what the Apps
            // Script function returns. Here, the function returns an Apps
            // Script Object with String keys and values, and so the result
            // is treated as a JavaScript object (folderSet).
            console.log(resp.result)
            var msg = resp.result.response.result;
            appendPre(msg);

            // do more stuff with the response code
        }
      })
    }

Apps脚本

Apps Script代码不需要进行太多修改。我们可以返回客户端使用的普通JSON对象,而不是使用ContentService返回。

function uploadDoc(e) {
  Logger.log(e);
  var returnValue = {};

  // Set the callback URL. See https://stackoverflow.com/questions/29525860/

  Logger.log("Uploading the document...");

  try { 
    // Get the file and create it in Drive
    var blob = UrlFetchApp.fetch(e).getBlob();
    DriveApp.createFile(blob);

    // If successful, return okay 
    var msg = "The document was successfully uploaded!";
    return msg;
  } catch(e) {
    Logger.log(e);
    // If a failure, return error message to the client
    return e.message
  }
}

我很难将CodePen列入白名单,所以I have an example hosted securely on my own site使用上面的代码。请随意检查来源并查看live Apps Script project

请注意,当您在Apps脚本项目中添加或更改范围时,用户需要重新授权。