3

How would I display an image embedded in a Tornado template, as opposed to having it rendered in a separate Tornado handler, as a page?

I want to serve an image via StringIO/ByesIO since I'll be eventually storing the image in a database.

I am sorry for including so much code. It might make the post too localized, but that code just loads an image.

Writing an image as a new page

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')

Template:


<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' codec can't decode byte 0xff in position 0: invalid start byte

  • What is the expected output? Why are you combining JPEG data and HTML with `image/jpg` MIME type? – Stefano Sanfilippo Jul 20 '14 at 15:29
  • The expected output is an image with other page content, e.g. the user's name, etc. I made these silly mistakes because I am unfamiliar with web development - I tend to stick to back-end development. –  Jul 20 '14 at 15:35

1 Answers1

3

Render the image in a different handler. Like:

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),
        ]

Template:

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

Of course, you could EMBED image into a webpage (like here: https://stackoverflow.com/a/4502403/207791), but it's rarely done, and the browser cache performance will suffer.

By the way, you might benefit from making image i/o asynchronous.

Community
  • 1
  • 1
Victor Sergienko
  • 13,115
  • 3
  • 57
  • 91