将内存流中的文件提供给 Bokeh 应用程序
Serve a file from a memory stream to a Bokeh app
我正在尝试向 Bokeh 网络应用程序的用户提供文件下载(.csv 和 .png)。目前这是通过在本地保存文件副本并执行读取该文件的 Javascript 来实现的(根据 this example and ):
def save_file(self, save_function, file_name):
"""
:param save_function: callable that takes a single argument: the file path to save to
:param file_name: default name for the downloaded file
"""
js_download = """
fetch(local_path, {cache: "no-store"}).then(response => response.blob())
.then(blob => {
//addresses IE
if (navigator.msSaveBlob) {
navigator.msSaveBlob(blob, save_name);
}
else {
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = save_name
link.target = '_blank'
link.style.visibility = 'hidden'
link.dispatchEvent(new MouseEvent('click'))
}
return response.text();
});
"""
# Make a tempory file name to avoid conflicts
temp_name = next(tempfile._get_candidate_names())
temp_path = f'static/{temp_name}.tmp'
save_function(temp_path)
self.exectute_js(CustomJS(args=dict(local_path='/AzDataTools/' + temp_path,
save_name=file_name), code=js_download))
def exectute_js(self, custom_js):
# Setup a dummy plot to trigger the javascript
dummy = self.get_any_figure().circle([1], [2], alpha=0)
dummy.glyph.js_on_change('size', custom_js)
dummy.glyph.size = 1
理想情况下,我希望通过保存到内存流来跳过本地文件步骤。我对此的尝试如下:
def save_file_via_memory(self, save_function, file_name):
"""
:param save_function: callable that takes a single argument: the file path to save to
:param file_name: default name for the downloaded file
"""
js_download = """
var blob = new Blob(data)
//addresses IE
if (navigator.msSaveBlob) {
navigator.msSaveBlob(blob, save_name);
}
else {
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = save_name
link.target = '_blank'
link.style.visibility = 'hidden'
link.dispatchEvent(new MouseEvent('click'))
};
"""
with io.BytesIO() as data:
save_function(data)
self.exectute_js(CustomJS(args=dict(data=data, save_name=file_name), code=js_download))
但是,这会产生错误,指示无法序列化字节流。有人知道如何将字节流传递给 JavaScript 吗?
(JavaScript也可能有误,第一次用。。。)
我最终采用的解决方法是将文件转换为文本字符串。对于 csvs 和图像,这看起来像:
def save_text_file(self, text, file_name):
"""
:param text: text to save
:param file_name: default name for the downloaded file
"""
js_download = """
const blob = new Blob([text_string], { type: 'text/csv;charset=utf-8;' })
//addresses IE
if (navigator.msSaveBlob) {
navigator.msSaveBlob(blob, file_name)
} else {
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = file_name
link.target = '_blank'
link.style.visibility = 'hidden'
link.dispatchEvent(new MouseEvent('click'))
}
"""
self.exectute_js(CustomJS(args=dict(text_string=text, file_name=file_name), code=js_download))
def image_to_png_string(image_bytes):
data_uri = base64.b64encode(image_bytes).decode('utf-8')
return 'data:image/png;base64,' + data_uri
def save_image_file(self, image, file_name):
"""
:param image: image bytes to save
:param file_name: default name for the downloaded file
"""
js_download = """
fetch(image_string, {cache: "no-store"}).then(response => response.blob())
.then(blob => {
//addresses IE
if (navigator.msSaveBlob) {
navigator.msSaveBlob(blob, file_name);
}
else {
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = file_name
link.target = '_blank'
link.style.visibility = 'hidden'
link.dispatchEvent(new MouseEvent('click'))
}
return response.text();
});
"""
self.exectute_js(CustomJS(args=dict(image_string=image_to_png_string(image), file_name=file_name),
code=js_download))
我正在尝试向 Bokeh 网络应用程序的用户提供文件下载(.csv 和 .png)。目前这是通过在本地保存文件副本并执行读取该文件的 Javascript 来实现的(根据 this example and
def save_file(self, save_function, file_name):
"""
:param save_function: callable that takes a single argument: the file path to save to
:param file_name: default name for the downloaded file
"""
js_download = """
fetch(local_path, {cache: "no-store"}).then(response => response.blob())
.then(blob => {
//addresses IE
if (navigator.msSaveBlob) {
navigator.msSaveBlob(blob, save_name);
}
else {
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = save_name
link.target = '_blank'
link.style.visibility = 'hidden'
link.dispatchEvent(new MouseEvent('click'))
}
return response.text();
});
"""
# Make a tempory file name to avoid conflicts
temp_name = next(tempfile._get_candidate_names())
temp_path = f'static/{temp_name}.tmp'
save_function(temp_path)
self.exectute_js(CustomJS(args=dict(local_path='/AzDataTools/' + temp_path,
save_name=file_name), code=js_download))
def exectute_js(self, custom_js):
# Setup a dummy plot to trigger the javascript
dummy = self.get_any_figure().circle([1], [2], alpha=0)
dummy.glyph.js_on_change('size', custom_js)
dummy.glyph.size = 1
理想情况下,我希望通过保存到内存流来跳过本地文件步骤。我对此的尝试如下:
def save_file_via_memory(self, save_function, file_name):
"""
:param save_function: callable that takes a single argument: the file path to save to
:param file_name: default name for the downloaded file
"""
js_download = """
var blob = new Blob(data)
//addresses IE
if (navigator.msSaveBlob) {
navigator.msSaveBlob(blob, save_name);
}
else {
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = save_name
link.target = '_blank'
link.style.visibility = 'hidden'
link.dispatchEvent(new MouseEvent('click'))
};
"""
with io.BytesIO() as data:
save_function(data)
self.exectute_js(CustomJS(args=dict(data=data, save_name=file_name), code=js_download))
但是,这会产生错误,指示无法序列化字节流。有人知道如何将字节流传递给 JavaScript 吗? (JavaScript也可能有误,第一次用。。。)
我最终采用的解决方法是将文件转换为文本字符串。对于 csvs 和图像,这看起来像:
def save_text_file(self, text, file_name):
"""
:param text: text to save
:param file_name: default name for the downloaded file
"""
js_download = """
const blob = new Blob([text_string], { type: 'text/csv;charset=utf-8;' })
//addresses IE
if (navigator.msSaveBlob) {
navigator.msSaveBlob(blob, file_name)
} else {
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = file_name
link.target = '_blank'
link.style.visibility = 'hidden'
link.dispatchEvent(new MouseEvent('click'))
}
"""
self.exectute_js(CustomJS(args=dict(text_string=text, file_name=file_name), code=js_download))
def image_to_png_string(image_bytes):
data_uri = base64.b64encode(image_bytes).decode('utf-8')
return 'data:image/png;base64,' + data_uri
def save_image_file(self, image, file_name):
"""
:param image: image bytes to save
:param file_name: default name for the downloaded file
"""
js_download = """
fetch(image_string, {cache: "no-store"}).then(response => response.blob())
.then(blob => {
//addresses IE
if (navigator.msSaveBlob) {
navigator.msSaveBlob(blob, file_name);
}
else {
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = file_name
link.target = '_blank'
link.style.visibility = 'hidden'
link.dispatchEvent(new MouseEvent('click'))
}
return response.text();
});
"""
self.exectute_js(CustomJS(args=dict(image_string=image_to_png_string(image), file_name=file_name),
code=js_download))