在Tornado模板中通过StringIO/BytesIO动态提供图像

3 投票
1 回答
4821 浏览
提问于 2025-04-18 14:01

我想知道如何在Tornado模板中显示一个嵌入的图片,而不是通过单独的Tornado处理程序来渲染一个页面。

我想通过StringIO/BytesIO来提供图片,因为我最终会把这个图片存储到数据库里。

抱歉代码写得有点多,可能会让这个帖子显得太局限,但这些代码只是用来加载一张图片。

将图片作为新页面写入

class DemoHandler(BaseHandler):
    @tornado.web.asynchronous
    def get(self):
        f = Image.open('img/cat.jpeg')
        o = io.BytesIO()
        f.save(o, format="JPEG")
        s = o.getvalue()
        self.set_header('Content-type', 'image/jpg')
        self.set_header('Content-length', len(s))   
        self.write(s)   
        self.render('demo.html')

模板:


<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=900">
</head>

<body>
  <div class="row">
        <div class="panel panel-default">
            <div class="panel-heading">User Account</div>
            <div class="panel-body" style="padding:5px">

                <div class="col-xs-3">
                    <img src={{ user['photo'] }} style="width:125px;height:125px;"/>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

Tornado:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import division

from base64 import b64encode
from PIL import Image
import tornado
import tornado.ioloop
import tornado.web
import collections
import logging
import re
import os
import io

from tornado.options import define, options
define('port', default=8888, help='Listening port', type=int)

class Application(tornado.web.Application):
    def __init__(self):
        handlers=[
            (r'/\/?', DemoHandler),
        ]
        settings = dict(
            template_path=os.path.dirname(os.path.abspath(__file__)),
            static_path=os.path.dirname(os.path.abspath(__file__)),
            xsrf_cookies=True,
            cookie_secret=b64encode(os.urandom(64)),
            debug=True,
            auto_reload=True,
        )
        tornado.web.Application.__init__(self, handlers, **settings)

        # Configure logging
        options.log_file_max_size = (1024**2)*10
        logging.getLogger().setLevel(logging.INFO)
        tornado.log.enable_pretty_logging()

        print("Listening on port: %d" % options.port)

class BaseHandler(tornado.web.RequestHandler):
    pass

class DemoHandler(BaseHandler):
    @tornado.web.asynchronous
    def get(self):
        f = Image.open('img/cat.jpg')
        o = io.BytesIO()
        f.save(o, format="JPEG")
        s = o.getvalue()    

        user = collections.defaultdict(lambda: collections.defaultdict(dict))
        user['photo']=s

        self.render('demo.html', user=user)

if __name__ == "__main__":
    tornado.options.parse_command_line()
    app = Application()
    app.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

UnicodeDecodeError: 'utf-8' 编码无法解码字节 0xff 在位置 0:无效的起始字节

1 个回答

3

在不同的处理程序中渲染图像。比如:

class ImageHandler(BaseHandler):
    @tornado.web.asynchronous
    def get(self, filename):
        f = Image.open('img/' + filename)
        o = io.BytesIO()
        f.save(o, format="JPEG")
        s = o.getvalue()
        self.set_header('Content-type', 'image/jpg')
        self.set_header('Content-length', len(s))   
        self.write(s)   


class PageHandler(BaseHandler):
    @tornado.web.asynchronous
    def get(self):
        self.render('demo.html', filename=)

class Application(tornado.web.Application):
    def __init__(self):
        handlers=[
            (r'/', PageHandler),
            (r'/img/(?P<filename>.+\.jpg)?', ImageHandler),
        ]

模板:

...
<img src="/img/{{filename}}" style="width:125px;height:125px;"/>
...

当然,你可以把图像嵌入到网页中(就像这里: https://stackoverflow.com/a/4502403/207791),但这样做的机会不多,而且会影响浏览器的缓存性能。

顺便提一下,你可以尝试让图像的输入输出变成异步的,这样可能会更有效率。

撰写回答