检查用户是否已运行它 [英] Check if user has run it

查看:111
本文介绍了检查用户是否已运行它的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I run a Google Apps script that uploads a file to the user's Google Drive file:

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

我的网站加载了一个弹出窗口,该窗口运行带有该代码的Google Apps脚本.效果很好.

My site loads a popup window that runs a Google Apps Script with that code. Works fine.

现在,如何与他们的用户成功上传文件的网站进行 back 通讯?在这种情况下,如何与用户进行服务器的回信交流?已经运行doGet()?`

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

Some type of response handling must exist?

完整的工作代码(在 JSBin 中进行测试 ):

Full working code (test it out on 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>

为澄清起见,我想要一种方法来确定用户是否已成功上传文件.像这样:

So to clarify, I want a way to find out whether if the user has successfully uploaded the file. Something like:

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

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

Adding a bounty for a complete solution to this.

推荐答案

除了返回HtmlService对象,您还可以使用jQuery的$.getJSON方法传递数据,并使用ContentServicedoGet函数检索数据. Google Apps脚本不接受CORS,因此使用JSONP是从脚本中获取数据的最佳方法.

查看此帖子.

Rather than returning a HtmlService object, you can pass data using jQuery's $.getJSON method and retrieve data from the doGet function with ContentService. Google Apps Script does not accept CORS, so using JSONP is the best way to get data to and from your script. See this post for more.

工作CodePen示例

Working CodePen Example

为了清楚起见,我拆分了HTML和脚本.与原始示例相比,HTML都没有变化.

I split your HTML and scripts for clarity. None of the HTML changed from your original example.

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参数中传递一个空字符串来测试错误消息.该消息将在控制台以及用户页面中返回.

You can test the error message by passing an empty string in the data-url param. The message is returned in the console as well as the page for the user.

上述解决方案在控制授权流程方面存在问题.在与驱动器工程师进行研究并交谈后(请参阅此处的线程),我将其重新设计为基于 Apps Script API 的自托管示例,并以API可执行文件而不是Apps Script Web App.这样,您就可以在Apps脚本网络应用程序之外访问[run](https://developers.google.com/apps-script/api/reference/rest/v1/scripts/run)方法.

The above solution has problems with controlling the authorization flow. After researching and speaking with a Drive engineer (see thread here) I've reworked this into a self-hosted example based on the Apps Script API and running the project as an API executable rather than an Apps Script Web App. This will allow you to access the [run](https://developers.google.com/apps-script/api/reference/rest/v1/scripts/run) method outside an Apps Script web app.

遵循针对JavaScript的Google Apps脚本API说明. Apps脚本项目应该是独立的(未链接到文档),并且应作为API可执行文件发布.您需要打开Cloud Console并创建OAuth凭据和API密钥.

Follow the Google Apps Script API instructions for JavaScript. The Apps Script project should be a standalone (not linked to a document) and published as API executable. You'll need to open the Cloud Console and create OAuth credentials and an API key.

说明要求您在计算机上使用Python服务器.我使用Node JS服务器http-server,但是您也可以将其在线运行并从那里进行测试.您需要在Cloud Console中将您的来源列入白名单.

The instructions have you use a Python server on your computer. I use the Node JS server, http-server, but you can also put it live online and test from there. You'll need to whitelist your source in the Cloud Console.

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

Since this is self hosted, you'll need a plain HTML page which authorizes the user through the OAuth2 API via JavaScript. This is preferrable because it keeps the user signed in, allowing for multiple API calls to your script without reauthorization. The code below works for this application and uses the authorization flow from the Google quickstart guides.

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脚本代码进行太多修改.除了使用ContentService返回之外,我们还可以返回客户端要使用的普通JSON对象.

Apps Script

The Apps Script code does not need to be modified much. Instead of returning using ContentService, we can return plain JSON objects to be used by the client.

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列入白名单,所以我有一个安全地托管在自己网站上的示例使用上面的代码.随时检查源代码并查看实时Apps脚本项目.

I had a hard time getting CodePen whitelisted, so I have an example hosted securely on my own site using the code above. Feel free to inspect the source and take a look at the live Apps Script project.

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

Note that the user will need to reauthorize as you add or change scopes in your Apps Script project.

这篇关于检查用户是否已运行它的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆