在 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 ...")
}
我有一个通过 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 ...")
}