小编典典

用Flask提供Matplotlib图像的成语之间有什么区别?

python

网络搜索变成了几个简单的(无证)的例子(和良好的答案在这里约)如何动态服务使用瓶Matplotlib数字; 但是这些功能的特点以及它们之间的差异使我感到困惑。

一些使用低级IO并返回元组

io = StringIO.StringIO()
plt.savefig(io, format='png')
io.seek(0)
data = io.read()
return data, 200, {'Content-type': 'image/png'}

而其他几个 使用不同的IO API并返回一个Response

io = StringIO.StringIO()
canvas = FigureCanvas(fig)
canvas.print_png(io)
response = make_response(io.getvalue())
response.mimetype = 'image/png' # or response.headers['Content-Type'] = 'image/png'
return response

而其他人则采用不同的方法来编码和构建返回值

io = StringIO.StringIO()
fig.savefig(io, format='png')
data = io.getvalue().encode('base64')
return html.format(data)

所有这些似乎都有效。但是我想知道它们是否共享这些方法的功能,或者它们之间的差异具有非显而易见的后果(例如,对于性能或对不同场景的适用性)。

第一,

扮演了什么角色StringIO; 这是准备服务(任何形式)图像的唯一方法吗?
在我的Python生活中,我从未见过使用过它,也不清楚为什么它似乎是服务器(二进制?)文件处理的必需部分。

其次,我想知道这些示例采用不同的方法来打包其响应。特别

使用seekplus read,vs getvalue.或具有相同的作用是否有意义?
是什么决定了返回方法的选择:元组vs. html.formatvs. Response(带有make_response);最后
为什么有些方法Content-type显式地设置显式,而另一些方法将编码(设置为“ base64”)?
这些方法中的任何一种是否被视为“最佳”或最新的惯用(或至少Pythonic)方法?


阅读 214

收藏
2020-12-20

共1个答案

小编典典

StringIO扮演什么角色;这是准备服务(任何形式的)图像的唯一方法吗?

首先,不,这不是唯一的方法。“经典”方式将涉及文件系统:

让matplotlib创建一个绘图。
将相应的图像数据持久保存到文件系统中的文件中(这涉及到上下文切换到调用诸如之类的系统调用的内核write())。
再次读取该文件的内容(这使内核可以通过来为您读出文件系统read())。
通过定义良好的数据编码以及正确设置的标头的HTTP响应,将内容提供给客户端。
步骤(3)和(4)涉及文件系统交互。也就是说,内核实际上与硬件组件进行通信。这需要时间(对于传统的hrad驱动器,由于访问时间长,因此仅向光盘写入几个字节可能需要几毫秒的时间)。现在,问题是:您是否需要将图像数据持久化到磁盘上?如果答案为“否”,则可以通过将图像数据保留在Web应用程序进程的内存中,从而跳过与文件系统的整个交互过程并节省一些时间。这对以下StringIO方面有好处:

StringIO是Python中非常通用的工具,可提供类似文件的对象,而实际数据永远不会委托给内核以将其写入文件系统或从文件系统读取。它保存在内存中。这就是为什么StringIO对象也称为内存中文件的原因。

问题的关键是,plt.savefig() 希望有一个对象作为第一个参数,看起来像,实际上代表在文件系统中实际文件的对象。StringIO提供了这样的对象,但是-在后台-将数据写入当前进程堆中的缓冲区,并在需要时再次从那里读取数据。

通过读取/写入一小部分数据StringIO需要花费纳秒或微秒的时间,而与文件系统的交互通常要慢几个数量级。

现在,请不要误会我的意思:通常,文件系统足够快,并且操作系统具有自己的技术来使文件系统交互尽可能快。如前所述,真正的问题是:您是否需要持久保存图像数据?如果以后不关心访问此图像数据,则不要涉及文件系统。这就是您显示的三个摘要的创建者所决定的。

出于性能原因,用StringIO替换实际文件系统交互可能是一个非常非常有效的决定。但是,您的Web应用程序中肯定还有其他瓶颈。例如,使用StringIO可以将请求响应延迟减少5毫秒。但是,考虑到网络延迟为100 ms,这实际上是否重要?另外,请记住,发送大型文件内容最好不要打扰一个严肃的Web应用程序-使用完善的Web服务器(也可以利用sendfile()系统调用)可以更好地满足这些要求。在这种情况下,让matplotlib将文件写入文件系统,然后告诉您的Web服务器(通过X-Sendfile标头)来完成剩下的工作。因此,性能是一个复杂的主题,可能不是最有力的论据。但是只有您知道您的要求!

使用搜索加读取与获取值之间是否有任何意义,或者这些操作实际上具有相同的作用

本质上是一样的。没有概念上的差异,也没有(重大的)性能差异。

是什么决定了返回方法的选择:元组vs. html.format vs. Response(带有make_response);最后

没有明确的答案。有很多方法可以将数据获取到客户端。没有“正确”的方法,只有更好或更糟。最好采用哪种方法在很大程度上取决于Web框架。使用Flask,make_response()是创建响应对象的规范方法。html.format()可能有一些我不知道的优点-您需要自己了解一下!但是,请继续阅读,我认为Flask中内置了一种完全适合您的情况的方法。

为什么有些方法显式设置Content-type,而另一些方法设置编码(为“ base64”)?

有通过HTTP将文件发送到浏览器的正确和不正确的方法。通常,HTTP响应应包含某些标头(另请参阅所需的HTTP响应标头)。仅出于您的理解,您可能需要阅读这些详细信息。当然,二进制数据需要使用客户端可以理解的编码进行编码,并且必须在响应标头中阐明该编码。另外,正确的HTTP响应应包含MIME类型(内容类型)。您所介绍的方法似乎并不能真正控制另一种方法(没有冒犯性,简单易懂的例子通常更着重于一件事而不是另一件事)。

我认为您确实应该使用Flask的send_file方法来为您处理一些重要的事情。此方法有两个参数。我会通过明确定义MIME类型mimetype。第一个参数可以是类似文件的对象,因此StringIO对象可以正常工作。但是,在这种情况下,您需要执行以下操作seek(0):

在调用send_file()之前,请确保文件指针位于要发送的数据的开头。

以下两种方法在语义上是优雅的(我认为),应适当注意对文件内容进行编码和设置HTTP响应标头:

from flask import send_file 

1)

f = StringIO.StringIO()
plt.savefig(f, format='png', dpi=300)
f.seek(0)
send_file(f, mimetype='image/png')

2)

plt.savefig('image.png', dpi=300)
send_file('image.png', mimetype='image/png')

在第二种情况下,如果正确配置,您的Web服务器(例如nginx)可以为您传输文件。

2020-12-20