我创建了一个用户界面来从MySQL表中获取数据并将其可视化。它在散景服务器上运行。我的用户使用他们的浏览器(firefox)远程连接到服务器。这非常合适:我只是将表导入到pandas数据帧中。
我的用户还需要以excel的形式下载该表。这意味着我无法使用纯{javascript的export_csv示例。
我没有使用JavaScript的经验。 我想要的只是将文件从我的main.py所在的目录传输到客户端。
我到目前为止尝试的技术是将正常的on_click
回调加入按钮,导出我需要的信息' output.xls',然后从虚拟字形中更改参数然后运行Javascript代码。我从Bokeh widgets call CustomJS and Python callback for single event?得到了这个想法。注意我还没有将alpha设置为0,这样我就可以看到圈子在点击下载按钮时是否真的在增长。
在我的邮件底部,您可以找到我的代码。您可以看到我已尝试使用XMLHttpRequest和直接使用Fetch。在前一种情况下,没有任何反应。在后一种情况下,我获得了一个名为&#34; mydata.xlsx&#34;的文件。正如预期的那样,它只包含 这个原始文本:<html><title>404: Not Found</title><body>404: Not Found</body></html>
。
代码:
p = figure(title='mydata')
#download button
download_b = Button(label="Download", button_type="success")
download_b.on_click(download)
#dummy idea from https://stackoverflow.com/questions/44212250/bokeh-widgets-call-customjs-and-python-callback-for-single-event
dummy = p.circle([1], [1],name='dummy')
JScode_xhr = """
var filename = p.title.text;
filename = filename.concat('.xlsx');
alert(filename);
var xhr = new XMLHttpRequest();
xhr.open('GET', '/output.xlsx', true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
if (this.status == 200) {
var blob = this.response;
alert('seems to work...');
if (navigator.msSaveBlob) {
navigator.msSaveBlob(blob, filename);
}
else {
var link = document.createElement("a");
link = document.createElement('a');
link.href = URL.createObjectURL(blob);
window.open(link.href, '_blank');
link.download = filename;
link.target = "_blank";
link.style.visibility = 'hidden';
link.dispatchEvent(new MouseEvent('click'));
URL.revokeObjectURL(url);
}
}
else {
alert('Ain't working!');
}
};
"""
JScode_fetch = """
var filename = p.title.text;
filename = filename.concat('.xlsx');
alert(filename);
fetch('/output.xlsx').then(response => response.blob())
.then(blob => {
alert(filename);
//addresses IE
if (navigator.msSaveBlob) {
navigator.msSaveBlob(blob, filename);
}
else {
var link = document.createElement("a");
link = document.createElement('a')
link.href = URL.createObjectURL(blob);
window.open(link.href, '_blank');
link.download = filename
link.target = "_blank";
link.style.visibility = 'hidden';
link.dispatchEvent(new MouseEvent('click'))
URL.revokeObjectURL(url);
}
return response.text();
});
"""
dummy.glyph.js_on_change('size', CustomJS(args=dict(p=p),
code=JScode_fetch))
plot_tab = Panel(child=row(download_b,p),
title="Plot",
closable=True,
name=str(self.test))
def download():
writer = pd.ExcelWriter('output.xlsx')
data.to_excel(writer,'data')
infos.to_excel(writer,'info')
dummy = p.select(name='dummy')[0]
dummy.glyph.size = dummy.glyph.size +1
答案 0 :(得分:1)
bokeh serve
只创建一些预定义处理程序来提供一些静态文件和WebSocket连接 - 默认情况下,它没有任何东西可以从项目的根目录中提供文件。
您可以尝试使用directory format,将文件保存到static
目录,然后从/static/
下载,而不是使用单文件格式。
这种方法的一个缺点是你仍然必须编写那些复杂的代码,只是让你的后端在用户下载之前创建文件。
最好的解决方案是更进一步,将Bokeh Server作为库嵌入到您的主应用程序中。由于您没有任何非Bokeh代码,最简单的方法是使用Tornado(an example)。
bokeh.server.server.Server
接受extra_patterns
参数 - 你可以在那里添加一个处理程序来动态创建Excel文件,并从/data/
中提供它们。毕竟,您在前端唯一需要的是Excel文件的单个链接。
答案 1 :(得分:1)
尝试Eugene Pakhomov的回答,我发现了问题所在。
我命名为JScode_fetch
的javascript代码几乎正确,但是我得到了404,因为它没有正确指向正确的路径。
我以目录格式创建了我的应用程序:我将.py文件更改为main.py
,将其放入名为app
的文件夹中,并在JScode_fetch
中更改了这一行代码:
fetch('/app/static/output.xlsx', {cache: "no-store"}).then(response => response.blob())
[...]
您可以看到问题在于它尝试访问localhost:5006/output.xlsx
而不是localhost:5006/app/output.xlsx
。由于它是目录格式,现在localhost:5006/app/static/output.xlsx
的正确链接将计入static
目录。
我还更改了download
函数中的几行:
def download():
dirpath = os.path.join(os.path.dirname(__file__),'static')
writer = pd.ExcelWriter(os.path.join(dirpath,'output.xlsx'))
writer = pd.ExcelWriter('output.xlsx')
data.to_excel(writer,'data')
infos.to_excel(writer,'info')
dummy = p.select(name='dummy')[0]
dummy.glyph.size = dummy.glyph.size +1
现在它正在完美运作!
编辑:我在, {cache: "no-store"}
函数中添加了fetch()
。否则,如果您在使用相同的output.xlsx
文件名时必须下载不同的数据帧excel,则浏览器认为该文件是相同的。更多信息here。