将文件从服务器发送到bokeh上的客户端 [英] send file from server to client on bokeh

查看:78
本文介绍了将文件从服务器发送到bokeh上的客户端的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经建立了一个用户界面来从MySQL表中获取数据并对其进行可视化.它在bokeh服务器上运行.我的用户使用他们的浏览器(firefox)远程连接到服务器.效果很好:我只是将表格导入到pandas数据框中.

I have made a user interface to fetch data from a MySQL table and visualize it. It is running on a bokeh server. My users connect remotely to the server using their browser (firefox). This works perfectly fine: I simply import the table into a pandas dataframe.

我的用户还需要以excel格式下载表格.这意味着我不能使用纯JavaScript的 export_csv 示例

My users also need to download the table as excel. This means I cannot use the export_csv example which is pure javascript.

我没有使用JavaScript的经验. 我要做的就是将文件从main.py所在的目录传输到客户端.

I have no experience with JavaScript. All I want is to transfer a file from the directory where my main.py is to the client side.

到目前为止,我尝试过的技术是将普通的on_click回调连接到按钮,将我需要的信息导出到'output.xls',然后从虚拟字形更改参数,然后再运行Javascript代码.我的想法来自 Bokeh小部件调用CustomJS和Python回调单个事件?.请注意,我尚未将Alpha设置为0,以便在单击下载"按钮后可以看到圆圈是否真的在增大.

The technique I have tried so far is to join a normal on_click callback to a button, export the information I need to 'output.xls', then change a parameter from a dummy glyph which in turn runs a Javascript code. I got the idea from Bokeh widgets call CustomJS and Python callback for single event? . Note I haven't set the alpha to 0, so that I can see if the circle is really growing upon clicking the download button.

在我的消息的底部,您可以找到我的代码.您可以看到我已经尝试同时使用XMLHttpRequest和Fetch.在前一种情况下,什么也没有发生.在后一种情况下,我会按预期获得名为"mydata.xlsx"的文件,但是该文件仅包含以下原始文本:<html><title>404: Not Found</title><body>404: Not Found</body></html>.

At the bottom of my message you can find my code. You can see I have tried with both XMLHttpRequest and with Fetch directly. In the former case, nothing happens. In the latter case I obtain a file named "mydata.xlsx" as expected, however it contains only this raw text: <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

推荐答案

尝试Eugene Pakhomov的答案,我发现了问题所在.

Trying out Eugene Pakhomov's answer, I found what was the issue.

我命名为JScode_fetch的javascript代码几乎是正确的,但是我得到了404,因为它未正确指向正确的路径.

The javascript code I named JScode_fetch is almost correct, however I get a 404 because it is not pointing correctly to the right path.

我以目录格式创建了应用程序:我将.py文件更改为main.py,将其放置在名为app的文件夹中,并在JScode_fetch中更改了这一行代码:

I made my application in the directory format: I changed my .py file to main.py, placed it into a folder called app, and changed this one line of code in 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目录.

You can see the problem was that it was trying to access localhost:5006/output.xlsx, instead of localhost:5006/app/output.xlsx. As it is in directory format, the right link is now localhost:5006/app/static/output.xlsx to count for the static directory.

我还在download函数中更改了几行:

I also changed a few lines in the download function:

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

现在它可以正常工作了!

Now it is working flawlessly!

我在fetch()函数中添加了, {cache: "no-store"}.否则,如果您必须使用相同的output.xlsx文件名下载其他数据框excel,则浏览器会认为该文件是相同的.更多信息此处.

edit: I have added , {cache: "no-store"} within the fetch() function. Otherwise the browser thinks the file is the same if you have to download a different dataframe excel while using the same output.xlsx filename. More info here.

这篇关于将文件从服务器发送到bokeh上的客户端的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆