【发布时间】:2019-12-16 00:28:29
【问题描述】:
文档参考文献:
- Drive file picker v3
- G Suite 对话:File Open Dialog
SO 参考文献:
- Access data in Google App Script from spread sheet modal html form
- How do I handle the call back using multiple Google File Picker
要达到什么目标?
在 Google 表格脚本中,我想定义一个文件选择器,它返回所拾取文件的 data,前提是调用者可以从脚本的另一部分接收那个data。
问题:
文件选择器作为html 模态对话框启动。找了一阵子,从启动picker的脚本中获取data的唯一解决方案是从html脚本代码:
- 将选择器的
callaback设置为特定功能:picker.setCallback(my_callback) - 或使用
google.script.run.my_callback(例如,来自按钮Done)
...前提是脚本中定义的my_callback 函数获得data。
上面的问题是你不能将同一个picker用于多种用途,因为:
-
my_callback在html脚本中固定 -
my_callback不知道最初调用picker的目的是什么(即它应该获取内容吗?,它应该将信息提供给一些未知的调用者吗?)。
一旦它得到data,my_callback 不知道如何处理它......除非my_callback 只与1 个调用者绑定;这似乎不正确,因为这需要为picker 定义多个html,每个您可以调用它的原因一次,因此它可以回调到正确的函数。
有什么想法吗?
- 脚本中的全局变量被重新初始化,并且不能使用PropertiesService 来存储
String以外的值(因此无法通过全局变量存储最终的picker_callback)。 -
google.script.run不会通过将服务器端函数的名称指定为String(reference) 来提供调用(它放弃了通过更改回调函数来生成picker_dialog.html的函数)。
示例代码
code.gs
function ui() {
return SpreadsheetApp.getUi();
}
function onOpen() {
ui().createMenu('ecoPortal Tools')
.addItem('Read a file', 'itemReadFile')
.addItem('Edit a file', 'itemEditFile')
.addToUi();
}
function itemReadFile() {
pickFile(readFile)
}
function itemEditFile() {
pickFile(editFile)
}
function readFile(data) {
/* do some stuff */
}
function editFile(data) {
/* do some stuff */
}
picker.gs:
function pickFile(callback) {
var html = HtmlService.createHtmlOutputFromFile('picker_dialog.html')
.setWidth(600)
.setHeight(425)
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
// concept (discarded):
callback_set('picker', callback)
ui().showModalDialog(html, 'Select a file');
}
function getOAuthToken() {
DriveApp.getRootFolder();
return ScriptApp.getOAuthToken();
}
// picker callback hub
function pickerCallback(data) {
var callback = callback_get('picker');
callback_set('picker', null);
if (callback) callback.call(data);
}
picker_dialog.html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons.css">
<script>
var DEVELOPER_KEY = '___PICKER_API_KEY_____';
var DIALOG_DIMENSIONS = {width: 600, height: 425};
var pickerApiLoaded = false;
// currently selected files data
var files_data = null;
/**
* Loads the Google Picker API.
*/
function onApiLoad() {
gapi.load('picker', {'callback': function() {
pickerApiLoaded = true;
}});
}
function getOAuthToken() {
console.log("going to call get auth token :)");
google.script.run.withSuccessHandler(createPicker)
.withFailureHandler(showError).getOAuthToken();
}
function createPicker(token) {
console.log("pickerApiLoadded", pickerApiLoaded);
console.log("token", token);
if (pickerApiLoaded && token) {
var picker = new google.picker.PickerBuilder()
.addView(google.picker.ViewId.DOCS)
.enableFeature(google.picker.Feature.NAV_HIDDEN)
.hideTitleBar()
.setOAuthToken(token)
.setDeveloperKey(DEVELOPER_KEY)
.setCallback(pickerCallback)
.setOrigin(google.script.host.origin)
.setSize(DIALOG_DIMENSIONS.width - 2,
DIALOG_DIMENSIONS.height - 2)
.build();
picker.setVisible(true);
} else {
showError('Unable to load the file picker.');
}
}
function pickerCallback(data) {
var action = data[google.picker.Response.ACTION];
if (action == google.picker.Action.PICKED) {
files_data = data;
var doc = data[google.picker.Response.DOCUMENTS][0];
var id = doc[google.picker.Document.ID];
var url = doc[google.picker.Document.URL];
var title = doc[google.picker.Document.NAME];
document.getElementById('result').innerHTML =
'<b>You chose:</b><br>Name: <a href="' + url + '">' + title +
'</a><br>ID: ' + id;
} else if (action == google.picker.Action.CANCEL) {
document.getElementById('result').innerHTML = 'Picker canceled.';
}
}
function showError(message) {
document.getElementById('result').innerHTML = 'Error: ' + message;
}
function closeIt() {
google.script.host.close();
}
function returnSelectedFilesData() {
google.script.run.withSuccessHandler(closeIt).pickerCallback(files_data);
}
</script>
</head>
<body>
<div>
<button onclick='getOAuthToken()'>Select a file</button>
<p id='result'></p>
<button onclick='returnSelectedFilesData()'>Done</button>
</div>
<script src="https://apis.google.com/js/api.js?onload=onApiLoad"></script>
</body>
</html>
【问题讨论】:
-
你读过this吗?多用途是什么意思?你的一些假设是错误的。但目前还不清楚你想做什么。
-
假设我想使用选取器对获得的文件做不同的事情:(a) 拾取文件以加载一些内容,(b) 拾取文件以读取一些定义并使用它们从应用程序中,(c) 选择一个文件来写入一些结果,(d) 选择一个文件来导入一些选项卡。使用您提到的示例,
callback在每种情况下都会有所不同,不是吗? -
仍然不确定你想要什么,但我会添加一个答案。
-
你能给出一个完整的概述吗?还是流量?类似于:用户想要获取文件的元数据>用户单击菜单>主功能>主功能打开选择器>用户在选择器中从他的驱动器中选择一个文件>文件从 html 发送到 gs/server=>服务器读取元数据文件并将其返回到 html>html 显示从服务器返回的数据=>成功
-
@TheMaster 我认为核心问题是我没有看到框架的客户端-服务器二元性。我正在考虑在服务器-客户端部分之间创建一个连续的工作流,尽管实际上存在分离的客户端和服务器端。
标签: google-apps-script google-picker