【问题标题】:How do I embed a truly live Google Sheets spreadsheet into a webpage?如何将真正实时的 Google 表格电子表格嵌入网页?
【发布时间】:2020-09-03 13:22:00
【问题描述】:

我想要什么

一个显示真正实时工作表的网站(当工作表从其他地方更改时立即更新,如在编辑器中),但位于屏幕中心且没有菜单等(如 2b 中)

具体来说是一个网站

  • 显示一张 Google 表格电子表格,格式正确
  • 大约每秒一次在没有任何用户输入的情况下实时更新工作表
  • 不包含 Google 表格编辑标题
  • 使页面中的内容居中,并在电子表格之外使用黑色边框填充屏幕

我知道的

经过多次 Google 搜索,我找到了两个符合我目标的结果:

1。没有菜单的 Google 表格编辑器

您可以直接在编辑器中显示工作表,只需将?rm=minimal 添加到 url,如下所示

https://docs.google.com/spreadsheets/d/SPREADSHEET_ID/view?rm=minimal#gid=SHEET_ID

这个

  • 在工作表更改时实时更新数据

但是

  • 显示行和列标题(A、B、C、...、1、2、3、...)
  • 显示工作表选择和“在下方插入 x 行”
  • 不居中且没有黑色背景

2。这个其他的 URL 东西

当您编辑 URL 并将 /edit... 替换为 /htmlembed/sheet?gid=SHEET_ID 时,如

https://docs.google.com/spreadsheets/u/0/d/SPREADSHEET_ID/htmlembed/sheet?gid=SHEET_ID

这个

  • 不包含任何标题或类似内容
  • 甚至允许我使用range=A1NOTATION 参数仅指定要显示的固定范围

它可以使用 GScript WebApp 进行扩展:

2b。 GScript 网络应用程序

(请注意,我使用绿色而不是黑色进行可视化)

在作为 WebApp 发布的 GScript doGet(e) 函数中使用此 URL 允许我进一步自定义它。我只是在原始源代码中添加了一个样式标签,并使用背景颜色和弹性显示来设置背景和居中内容。这是我的功能,非常容易受到 HTML 注入:

function doGet(e) {
  // Getting spreadsheet app
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  // Getting sheet
  var sheet = ss.getSheetByName("Monitor " + e.parameter.monitor);
  //Return error if specified sheet does not exist
  if (sheet == null)
    return HtmlService.createHtmlOutput("<b>Invalid monitor id \"" + e.parameter.monitor + "\"</b> pass as ?monitor=MONITOR");

  // Generating the URL
  var publishedURL = "https://docs.google.com/spreadsheets/u/0/d/" + ss.getId() + "/htmlembed/sheet?range=a3:z&gid=" + sheet.getSheetId();

  // Fetching the site
  var response = UrlFetchApp.fetch(publishedURL, {'muteHttpExceptions': true}).getContentText();

  // Getting the background color from paramter (default is black)
  var bg = e.parameter.bg;
  if (bg == null)
    var bg = "black";

  // Defining the styling (I know this way is lazy)
  var styling = "<style>\
body, div {\
background-color: " + bg + " !important;\
display: flex;\
justify-content: center;\
align-items: center;\
}\
</style>";

  // Returning the webpage from original data combined with styling
  return HtmlService.createHtmlOutput(response+styling);
}

这在页面中进一步居中,并有一个黑色边框来填充电子表格之外的屏幕

但 URL 方法有一个非常明显的缺点:它不会每秒更新,但只有在页面刷新时才更新

然后我尝试了什么

通过 html 或 js 每秒刷新一次网页

这应该可以,但是由于页面加载“如此缓慢”,如果我每秒刷新一次,我会在一半的时间看到一个空白页面

从客户端获取 URL

利用 js fetch 函数,我可以在后台获取客户端上的源代码,然后更新速度会更快,但我遇到了跨域资源共享 (CORS) 问题,因为 Google 不允许我这样做当请求来自客户端时获取源。 (当我在 GScript 中获取它时,它确实有效。)

通过 WebApp 从客户端获取源

我的最后一个解决方案是从 WebApp 获取源代码,实习生从电子表格中获取源代码,但显然我不能允许 WebApp 使用 CORS。

我不知道的事情

如何获得 a) 即时更新和 b) 格式正确的中间地带?

我还能对 URL 做些什么吗?喜欢 /htmlembed

https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/gviz/tq?tqx=out:html&amp;tq&amp;gid=0

in this medium post所述

【问题讨论】:

  • 我有一个网络应用程序,允许我通过 html 表格访问和编辑我的任何电子表格。我想可以添加存储电子表格编辑日志的功能,并在每次客户端轮询启动该操作时更新每个单元格。这是它的链接。 html spreadsheet欢迎您尝试。
  • 感谢您的评论,不幸的是,这是错误的方法,我想在电子表格更改时更新网站。在这种情况下,我没有客户端触发器
  • HTML 中的每个单元格都是可寻址的,因此很容易通过客户端轮询源和 google.script.run 客户端到服务器通信从电子表格中添加更新 html,但是它并不完整必须自己写
  • 如果工作表是公开共享的,请尝试构建 url2b 而不是获取它。 &lt;iframe src="url"&gt;&lt;/iframe&gt;
  • 缓存response> 每秒,仅当response 与之前缓存的响应不同时,重新加载页面。

标签: google-apps-script google-sheets web-applications embed


【解决方案1】:

可以通过缓存 fetch 函数的响应并仅在页面发生更改时刷新页面来做到这一点,就像 @TheMaster 建议的那样。我还从 this post 添加了一个简单的哈希函数,并使用正则表达式来保护代码免受 HTML 注入的影响。

以下代码将在最后一次更新完成后立即刷新页面(大约每秒一次)。这仍然比在编辑器中要慢,因此您可能希望在原始问题中使用解决方案 1。

monitor.gs

/**
 * Only needs acced to the spredsheet the code is installed in
 * @OnlyCurrentDoc
 */

function doGet(e) {
  return HtmlService.createHtmlOutputFromFile("frame");
}


// Fetching the live content from URL
function fetchContent(publishedURL, e) {
  // Fetching the site
  var response = UrlFetchApp.fetch(publishedURL, {'muteHttpExceptions': true}).getContentText();

  // Getting the background color from paramter (default is black)
  var bg = e.parameter.bg;
  if (bg == null)
    var bg = "black";

  // Creating and returning the response
  var template = HtmlService.createTemplateFromFile("style");
  template.bg = /\w+/.exec(bg)[0]; // Setting the background-color
  return template.evaluate().append(response);
}

// Returns the live content if it has cahnged, null otherways
function getContent(e, currentHash) {
  // Getting spreadsheet app
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  // Getting sheet
  var sheet = ss.getSheetByName("Monitor " + e.parameter.monitor);
  //Return error if specified sheet does not exist
  if (sheet == null)
    return {content: "<b>Invalid monitor id \"" + /\w+/.exec(e.parameter.monitor)[0] + "\"</b> pass as ?monitor=MONITOR"};

  // Generating the URL
  var publishedURL = "https://docs.google.com/spreadsheets/u/0/d/" + ss.getId() + "/htmlembed/sheet?range=a3:z&gid=" + sheet.getSheetId();

  // Returning the content if it is different, null otherways
  var content = fetchContent(publishedURL, e).getContent();
  var hash = strhash(content);
  if (hash == currentHash)
    return null;
  Logger.log(hash);
  return {content: content, hash: hash};
}

(也附加this code

frame.html

<!DOCTYPE html>
<html>
  <head>
  <style>
  html {
  display: flex;
  justify-content: center;
  align-items: center;
  }
  </style>
  <script>
  let currentContent = undefined;
  function updateContent(content) {
  let doc = new DOMParser().parseFromString(content, "text/html")
  let sheets_viewport = doc.getElementById("sheets-viewport");

  console.log("Current hash: " + currentContent);
  if (content !== null) {
  document.open();
  document.write(content.content);
  document.close();

  console.log("refreshed.");
  currentContent = content.hash;
  console.log("New hash: " + currentContent);
  } else
  console.log("Nothing to refresh.");

  refresh();
  }
  function go(location) {
  google.script.run.withSuccessHandler(updateContent).getContent(location, currentContent);
  }
  refresh();
  function refresh() {console.log("refreshing..."); google.script.url.getLocation(go);}
  </script>
  </head>
  <body>
<div>
<p>Loading...</p>
</div>
  </body>
</html>

style.html

<style>
body, div {
background-color: <?= bg ?> !important;
display: flex;
justify-content: center;
align-items: center;
}
</style>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-21
    • 1970-01-01
    • 2011-04-27
    • 2019-07-25
    • 1970-01-01
    相关资源
    最近更新 更多