在 Google Apps/Sheets 中仅将可见的行和列下载为 CSV

Download only visible rows and columns as CSV in Google Apps/Sheets

我有一个通过 Apps 脚本在 Google Sheet 上生成的自定义侧边栏,它同时具有视图选项“过滤器”(shows/hides 某些列和行的单选按钮)使用“导出 CSV”按钮。

预期的功能是让用户select他们想要的查看选项并单击导出 CSV 按钮,CSV 文件将添加到他们的下载队列中。生成的 CSV 文件将仅包含屏幕上可见的行和列(由于查看选项)。

以下代码成功下载 CSV 文件,但包含可见和隐藏 rows/columns:

HTML CSV 导出按钮

<h4>Export as CSV</h4>
    <form id="thisSheetForm">
      <button class="red" onclick="download('csv')">Export as CSV</button>
    </form>

sidebar.html

中的 JS 函数处理程序
<script>
      function download(type) {
        google.script.run
        .withSuccessHandler(({ data, filename }) => {
          if (data && filename) {
            const a = document.createElement("a");
            document.body.appendChild(a);
            a.download = filename;
            a.href = data;
            a.click();
          }
        })
        .createDataUrl(type);
      }
    </script>

函数在code.gs

function createDataUrl(type) {
  const mimeTypes = { csv: MimeType.CSV, pdf: MimeType.PDF };
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getActiveSheet();
  let url = null;
  if (type == "csv") {
    url = `https://docs.google.com/spreadsheets/d/${ss.getId()}/export?format=csv&gid=${sheet.getSheetId()}`;
  } else if (type == "pdf") {
    url = `https://docs.google.com/spreadsheets/d/${ss.getId()}/export?format=pdf&gid=${sheet.getSheetId()}`;
  }
  if (url) {
    const blob = UrlFetchApp.fetch(url, {
      headers: { authorization: `Bearer ${ScriptApp.getOAuthToken()}` },
    }).getBlob();
    return {
      data:
        `data:${mimeTypes[type]};base64,` +
        Utilities.base64Encode(blob.getBytes()),
      filename: `${sheet.getSheetName()}.${type}`,
    };
  }
  return { data: null, filename: null };
}

上面的 createDataUrl() 函数是我成功下载 CSV 文件的唯一方法。我尝试过的所有其他选项要么引发 javascript 错误 re: security,要么只是将其下载到云端硬盘而不是添加到用户浏览器下载队列中。

但我现在正在努力了解如何操作该函数,以便生成的 CSV 数据仅包含我想要的列和行。有没有办法修改 url 以仅实现某些 columns/rows 或者我想做的根本不可能?

如有任何帮助,我们将不胜感激。

我相信你的目标如下。

  • 您的 sheet 隐藏了行和列。
  • 您想通过单击按钮过滤行和列,将 sheet 导出为 CSV 数据。

修改点:

  • 在您的 Google Apps 脚本中,导出了整个 sheet 值。
  • 在你的 HTML 和 Javascript 中,我担心点击按钮时, withSuccessHandler 不是 运行 重定向。

当这些点在你的脚本中体现出来,下面的修改怎么样?

修改后的脚本:

HTML & Javascript: sidebar.html

<body>
<h4>Export as CSV</h4>
<form id="thisSheetForm">
  <button class="red" onclick="download(); return false;">Export as CSV</button>
</form>
</body>
<script>
function download(type) {
  google.script.run
  .withSuccessHandler(({ data, filename }) => {
    if (data && filename) {
      const a = document.createElement("a");
      document.body.appendChild(a);
      a.download = filename;
      a.href = data;
      a.click();
    }
  })
  .createDataUrl(type);
}
</script>

Google 应用脚本:code.gs

function createDataUrl(type) {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getActiveSheet();
  const url = `https://docs.google.com/spreadsheets/d/${ss.getId()}/gviz/tq?tqx=out:csv&gid=${sheet.getSheetId()}`;
  const csv = UrlFetchApp.fetch(url, { headers: { authorization: `Bearer ${ScriptApp.getOAuthToken()}` } }).getContentText();
  const ar = Utilities.parseCsv(csv);
  const hiddenColumns = ar[0].reduce((col, _, i) => {
    if (!sheet.isColumnHiddenByUser(i + 1)) col.push(i);
    return col;
  }, []);
  const str = ar.map(r => hiddenColumns.map(c => isNaN(r[c]) ? `"${r[c].replace('"', '\"')}"` : r[c]).join(",")).join("\n"); // Modified
  const blob = Utilities.newBlob(str);
  return { data: `data:text/csv;base64,` + Utilities.base64Encode(blob.getBytes()), filename: `${sheet.getSheetName()}.csv` };
}
  • 当此脚本为 运行 时,通过过滤隐藏的行和列,活动的 sheet 被导出为 CSV 数据。

参考:

已添加:

I can't seem to get the above code to work on filtering out the rows - either when they're hidden via hide row or via a filter view.开始,我又测试了这个情况。由此发现,当手动操作和基本过滤器都隐藏行时,似乎 https://docs.google.com/spreadsheets/d/${ss.getId()}/gviz/tq?tqx=out:csv&gid=${sheet.getSheetId()} 端点检索到的 CSV 数据已损坏。从这种情况来看,在这种情况下,我想建议使用 Sheets API 而不是上面的端点。 Ref使用这个时,请修改上面的“Google Apps Script: code.gs”如下。

Google 应用脚本:code.gs

Please enable Sheets API at Advanced Google services。在这种情况下,通过一次 API 调用检索筛选的行和列。

function createDataUrl() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getActiveSheet();
  const res = Sheets.Spreadsheets.get(ss.getId(), { ranges: [sheet.getSheetName()], fields: "sheets/data" });
  const showRows = res.sheets[0].data[0].rowMetadata.flatMap(({ hiddenByFilter, hiddenByUser }, i) => hiddenByFilter || hiddenByUser ? i : []);
  const showCols = res.sheets[0].data[0].columnMetadata.flatMap(({ hiddenByFilter, hiddenByUser }, i) => hiddenByFilter || hiddenByUser ? i : []);
  const values = sheet.getDataRange().getDisplayValues().filter((_, i) => !showRows.includes(i)).map(r => r.filter((_, j) => !showCols.includes(j)));
  const str = values.map(r => r.map(c => isNaN(c) ? `"${c.replace('"', '\"')}"` : c).join(",")).join("\n");
  const blob = Utilities.newBlob(str);
  return { data: `data:text/csv;base64,` + Utilities.base64Encode(blob.getBytes()), filename: `${sheet.getSheetName()}.csv` };
}

您将只需要通过

获取可见的单元格

参考文献

在 gs 中尝试(没有特定的 html 文件)

function onOpen() {
  SpreadsheetApp.getUi().createMenu('⇩ M E N U ⇩')
    .addItem(' Download file ...', 'downloadCSVOnlyVisible')
    .addToUi();
}
function downloadCSVOnlyVisible() {
  const ss = SpreadsheetApp.getActiveSpreadsheet()
  const sh = ss.getActiveSheet()
  let source = []
  const sep = ',';
  sh.getDataRange().getValues().forEach((r, i) => {
    if (!sh.isRowHiddenByFilter(i + 1) && !sh.isRowHiddenByUser(i + 1)) {
      let prov = []
      r.forEach((c, j) => {
        if (!sh.isColumnHiddenByUser(j + 1)) {
          prov.push(isNaN(c) ? (c.includes(sep) ? `"${c.replace('"', '\"')}"` : c) : c)
        }
      })
      source.push([prov])
    }
  })
  const content = source.map(r => r.join(sep) + '\n').join('');
  const type = 'csv'
  const mimeTypes = { csv: MimeType.CSV };
  const name = ss.getName() + ' ' + sh.getName() + '.csv'
  const id = DriveApp.createFile(name, content).getId();
  const blob = DriveApp.getFileById(id).getBlob();
  const infoHtml = {
    data: `data:${mimeTypes[type]};base64,` + Utilities.base64Encode(blob.getBytes()),
    filename: `${name}`,
  };
  const html = HtmlService.createHtmlOutput(`<a href="${infoHtml.data}" download="${infoHtml.filename}">${infoHtml.filename}</a>`)
    .setWidth(420).setHeight(100);
  SpreadsheetApp.getUi().showModalDialog(html, "Download your file ...")
}