如何使用CherryPy在HTTP响应中返回图像

9

我有一段生成Cairo ImageSurface的代码,并且我像这样将其公开:

def preview(...):
    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
    ...
    cherrypy.response.headers['Content-Type'] = "image/png"
    return surface.get_data()
preview.exposed = True

这样不起作用(浏览器报告图像存在错误)。

我已经测试了surface.write_to_png('test.png')是可以工作的,但我不确定将数据倒入哪个对象才能返回它。我猜想是一些类似文件的对象?根据pycairo文档get_data()返回一个缓冲区。我现在也尝试过:

tempf = os.tmpfile()
surface.write_to_png(tempf)
return tempf

此外,是将此图像创建并保存在内存中(就像我正在尝试的那样)更好,还是将其写入磁盘作为临时文件并从那里提供服务?我只需要这张图片一次,然后就可以丢弃它。

write_to_png_stream 怎么样?http://cairographics.org/documentation/cairomm/reference/classCairo_1_1Surface.html#b3eca5bc13abe27f470fdf08134269bb - Pekka
pycairo似乎没有暴露那个方法... http://cairographics.org/documentation/pycairo/2/reference/surfaces.html#class-imagesurface-surface - colinmarc
4个回答

18

添加以下导入:

from cherrypy.lib import file_generator
import StringIO

然后像这样进行:

def index(self):
    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
    cherrypy.response.headers['Content-Type'] = "image/png"

    buffer = StringIO.StringIO()
    surface.write_to_png(buffer)
    buffer.seek(0)

    return file_generator(buffer)

另外,如果你正在提供一个独立的文件(即它不是网页的一部分),并且你不希望它被渲染到浏览器中,而是将其作为一个要保存到磁盘上的文件处理,则需要添加一个额外的头文件:

cherrypy.response.headers['Content-Disposition'] = 'attachment; filename="file.png"'
此外,如果你只是想将这个文件提供给浏览器,没有必要在服务器上将其创建到磁盘上。相反,应该记住访问硬盘会影响性能,因此不如直接在内存中生成并提供服务。

非常好!关于赏金,您能向我解释一下原因吗? - colinmarc
很棒的回答,zifot - 我认为关键是使用“write_to_png”将原始数据转换为PNG格式。仅使用surface.get_data()是不起作用的,因为它没有以PNG数据的形式存储(它是ARGB_32)。 - Marc Novakowski
@Marc Novakowski - 谢谢。你是对的,ImageSurface在内存中以这里描述的格式之一保存数据:http://cairographics.org/manual/cairo-image-surface.html#cairo-format-t。输出文件的格式则是另一回事。 - zifot
@colinmarc - 据我所知,如果您的问题有赏金,那么颁发奖励和接受答案(现在是两个单独的操作)只有在创建悬赏后两天之后才能进行。 - zifot
非常感谢,我真的卡了好一段时间。=) - colinmarc

2
你失败了,因为你不理解 surface.get_data() 的工作原理。 你试图返回 mime-type image/png,但是 surface.get_data() 返回的是普通位图图像(不是带有头文件的 Windows 位图文件 .BMP),它是从“虚拟屏幕”(surface)中导出的纯图像转储。
就像这样:
0000010000
0000101000
0001000100
0010000010
0001000100
0000101000
0000010000

0
尝试使用“内存文件”方法:
return StringIO.StringIO(surface.get_data())

0

你尝试过return str(surface.get_data())吗?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接