在散景

时间:2018-03-20 15:43:10

标签: javascript python python-3.x bokeh

我创建了一个用户界面来从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

2 个答案:

答案 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