使用Tornado无文件I/O服务图片

2 投票
3 回答
8500 浏览
提问于 2025-04-18 10:13

我正在尝试使用Tornado库来提供一个网络摄像头的图像,但我发现的唯一方法是先把图像保存下来,然后再返回图像的名称。

有没有办法可以直接提供图像,而不需要先保存到硬盘上呢?

import tornado.ioloop
import tornado.web
import pygame.camera
import pygame.image
from time import time
from io import StringIO

pygame.camera.init()
cam = pygame.camera.Camera(pygame.camera.list_cameras()[0])
cam.start()

class MainHandler(tornado.web.RequestHandler):
    def get(self):


        img = cam.get_image()
        name = str( round( time() ) )
        name = name + '.jpg'
        pygame.image.save(img, name)


        self.write('<img src="' + name + '">')



application = tornado.web.Application([
    (r"/", MainHandler),
    (r'/(.*)', tornado.web.StaticFileHandler, {'path': ''})
])

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

3 个回答

-1

当然!像这样的:

class ImageHandler(tornado.web.RequestHandler):
    def get(self):
        img = cam.get_image()
        self.set_header('Content-Type', 'image/jpg')
        self.write(img.contents)

application = tornado.web.Application([
    (r"/image.jpg", ImageHandler),
])

我不太清楚怎么把图片的内容转成字节,但你明白我的意思。

1

是的,你可以在不处理文件输入输出的情况下提供图片。我有一个用 python3 写的 应用程序,它通过发送一个1x1像素的图片来追踪用户,使用的是 Tornado

我在 源代码 中这样存储图片:

# 1x1 Transparent Pixel in HEX Format
pixel_GIF = [0x47,0x49,0x46,0x38,0x39,0x61,
             0x01,0x00,0x01,0x00,0x80,0x00,
             0x00,0x00,0x00,0x00,0xff,0xff,
             0xff,0x21,0xf9,0x04,0x01,0x00,
             0x00,0x00,0x00,0x2c,0x00,0x00,
             0x00,0x00,0x01,0x00,0x01,0x00, 
             0x00,0x02,0x01,0x44,0x00,0x3b]

然后,我使用下面的 函数 将图片从 HEX 格式转换为二进制:

def pack_into_binary(data):
    """Convert given data into binary form"""
    packed = str()
    for datum in data:
        packed += struct.pack('B', datum).decode("ISO-8859-1")
    return packed

pixel_binary = pack_into_binary(pixel_GIF)

之后,我 设置合适的头信息,然后 提供图片

#1x1 Transparent Tracking Pixel
self.set_header("Content-Length", 42)
self.set_header("Content-Type", "image/gif")
self.set_header("Pragma", "no-cache")
self.set_header("Cache-Control", 
                "no-store, "
                "no-cache=Set-Cookie, "
                "proxy-revalidate, "
                "max-age=0, "
                "post-check=0, pre-check=0"
                )
self.set_header("Expires", "Wed, 2 Dec 1837 21:00:12 GMT")
self.write(self.pixel_binary)

这一切都不需要进行任何 文件输入输出

注意:如果你手头有图片在内存中,你可以通过将格式转换为二进制并使用 write 方法来提供它。不同之处在于,你的图片不是预先确定的,而是动态生成的。

编辑:下面是从 RGB Surface 转换到 binary 编码的示例。

from StringIO import StringIO
from PIL import Image

data = pygame.image.tostring(cam.get_image(),"RGB")

img = Image.fromstring('RGBA', (100,200), data) # 100 x 200 example surface
zdata = StringIO()

img.save(zdata, 'JPEG')

self.write(zdata.getvalue())

然后你可以使用 Tornado 来提供 image

4

看起来pygame不支持直接把图片保存到像文件一样的对象里,所以你不能直接用它。不过,它有一个叫做 tostring 的方法。文档里提到,这个方法可以和其他图片库一起使用:

它会创建一个字符串,可以用其他Python图像库的‘fromstring’方法来传输

所以,你可以用 tostring 把你的图片转换成一个字符串,然后使用另一个支持将图片写入像文件一样对象的Python库,利用它的 fromstring 方法来处理。

下面是一个使用 pillow 作为替代图片库的例子。

import tornado.ioloop
import tornado.web
from PIL import Image
import cStringIO as StringIO

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("<img src='http://localhost:8888/img'>")

class ImgHandler(tornado.web.RequestHandler):
    img_name = "bg.jpg"
    img = pygame.image.load(img_name)
    str_img = pygame.image.tostring(img, "RGB")
    size = img.get_size()
    fimg = Image.frombytes("RGB", size, str_img, "raw")
    fobj = StringIO.StringIO()
    fimg.save(fobj, format="png")  #jpeg encoder isn't available in my install...
    for line in fobj.getvalue():
        self.write(line)
    self.set_header("Content-type",  "image/png")


application = tornado.web.Application([
    (r"/", MainHandler),
    (r"/img", ImgHandler),
    #(r'/(.*)', tornado.web.StaticFileHandler, {'path': ''})
])

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

访问 localhost:8888localhost:8888/img 都可以显示这张图片。

撰写回答