跳到主要内容

使用服务

本章介绍了ServiceData的用法:

service = ServiceData(cache: CacheAbstract, service_name: str = "/cached-data", chunk_size: int = 1)

dash中使用service

service主要用来为dash提供服务。它实现了一种由callback加载、且由下载链接获取的“动态资源(dynamical assets)”。

service的实例应当用作全局变量。换言之,该实例需要:

  1. 能被callback访问,这些callback负责存储数据到缓存。
  2. 能被dash.Dash()应用、在运行app.run()之前访问。

对于服务的基础用法,如下所示:

use_service_dash.py
import io
import dash
from dash import Output, Input
import dash_file_cache as dfc

app = dash.Dash("demo")
service = dfc.ServiceData(dfc.CachePlain(1))
service.serve(app)

app.layout = ...


@app.callback(Output(..., "src"), Input(...))
def a_callback_creating_data(...):
fobj = io.BytesIO()
fobj.write(...) # 于此生成一些数据。
address = service.register(
fobj=fobj,
mime_type="image/svg+xml",
one_time_service=True,
)
return address


if __name__ == "__main__":
app.run()

service的使用可以分成三个步骤:

  1. 初始化后,调用service.serve(app)以将服务(flask.views.View)绑定到应用上。
  2. 在callback中,调用service.register(...),将数据动态地置入缓存中。
  3. service.register(...)的返回值是指向缓存数据的地址。该地址可以用作<img>src属性或<object>data属性。

不同于应用的静态资源(assets),service.register(...)所返回的地址address,具有严格限定 在dashboard运行期间的生命周期。能取得返回值address,即可确保数据已俟访问。

service.register(...)有如下参数:

参数
说明
fobj所要提供的数据路径、或类文件对象。
file_namefobj的文件名。只有download参数指定时,才会用到该值。此时,该值将用作所下载文件的文件名。
content_type标记在HTML响应头中的内容类型。若该值留空,则将mime_type用作content_type。详情参见此处
mime_type所要提供的文件媒体类型。每个文件都对应一种媒体类型,详情参见此处。媒体类型总表参见此处
one_time_service二值量。标记有效时,会在文件送出之后,试图将文件清出缓存。设置该值可以节约缓存空间。
download二值量。标记有效时,返回的地址会被标记为用于下载文件。访问这样的地址会触发下载、而不是触发浏览器访问。
注意

若dashboard需要支持多用户同时访问、或缓存的数据需要访问多次时,不宜设置one_time_service=True

用于背景callbck

某些情况下,用户可能需要展示准备文件的进度。背景callback是一个合适的解决方案。在此情况下,callback需要做出略微调整。

use_service_dash.py
@app.callback(
Output(..., "src"),
Input(...),
background=True,
progress=[Output("prog", "children")],
manager=background_callback_manager,
prevent_initial_call=True,
)
def a_callback_creating_data(set_progress, ...):
fobj = io.BytesIO()
for i in range(...):
fobj.write(...) # 于此生成一些数据。
current_progress: int = ...
set_progress(("{0}%".format(current_progress),))
address = service.register(
fobj=fobj,
mime_type="image/svg+xml",
one_time_service=True,
)
return address

运行背景callback时,callback本身会被multiprocess分发到子进程中。若用户仍选择使用CachePlain来初始化ServiceData,对缓存的修改则只能在callback内部生效。一旦callback运行结束,在子进程做出的任何修改都会被舍弃。因此,callback会返回一个指向不存在文件的地址。要解决这个问题,需要使用

ServiceData(dfc.CacheQueue(cache_size: int, qobj: queue.Queue))

抑或是

ServiceData(dfc.CacheFile(cache_dir: str | None = None, chunk_size: int = 1))

flask中使用service

ServiceData与纯flask应用兼容,因为ServiceData完全由flask实现。

与使用Dash()相若,ServiceData仍需要同时满足被应用本身、以及被用户定义的服务访问。流程可分为两步:

  1. 定义一个用于接受数据查询请求的、POST方法服务。服务的响应提供的是用来访问缓存的地址。
  2. 使用缓存访问地址、和GET方法,来访问缓存数据。

以下展示了一个简单的范例:

use_service_flask.py
import io
import flask
import dash_file_cache as dfc

app = flask.Flask("demo")
service = dfc.ServiceData(dfc.CachePlain(1))
service.serve(app)


@app.route("/create-file", methods=["POST"])
def create_file():
address = service.register(
fobj=io.StringIO("a simple test"), file_name="test.txt", mime_type="text/plain"
)
return address


if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000)

要检验该应用,可以利用urllib发送一个POST请求,以获取缓存地址。urllib是Python标准库的一部分。

python -c "from urllib import request;print(request.urlopen(request.Request('http://127.0.0.1:5000/create-file', method='POST')).read().decode())"

该命令将会返回形如/cache-data?uid=...的地址。用户可按照如下方法,在浏览器中使用该地址:

http://127.0.0.1:5000/cache-data?uid=...

访问该地址后,可以透过浏览器见到如下纯文字:

a simple test
信息

上例中,可以在访问地址的末尾、简单地添加如下参数

http://127.0.0.1:5000/cache-data?uid=...&download=true

则,浏览器会下载文件、而非访问文件。