0

I've created an entity 'Images' which stored the file name and a blob object for storing images:

class Images(ndb.Model):
    file_name = ndb.StringProperty()
    blob = ndb.BlobProperty()

Now, I have tried to display the 'database.jpeg' image in my application as follows:

HTML form

<html>
<head>
    <title>Template</title>
</head>
<body>
    <img src="/img_serve/database.jpeg" /> 
</body>
</html>

Python file:

class ImgServe(Handler):
    def get(self, resource):
        image = ndb.Key('Images', resource).get()
        self.response.headers[b'Content-Type'] = mimetypes.guess_type(image.file_name)[0]
        self.response.write(image.blob)  

app = webapp2.WSGIApplication([('/', MainPage),
                                ('/signup', Register),
                                ('/login', Login),
                                ('/logout', Logout),
                                ('/mp', MP),
                                (r'/file_upload', FileUpload),
                                ('/blob', Blob),
                                (r'/img_serve/<resource:(.*)>', ImgServe),
                                ('/template', Template)], debug=True)

Here's a screenshot of the 'Images' entity:

enter image description here

However, I get a broken image in my application. What seems to be the problem in my code?


The whole code:

main.py

import os
import re
import random
import hashlib
import hmac
from string import letters
import mimetypes
import webapp2
import jinja2

from google.appengine.ext import ndb

template_dir = os.path.join(os.path.dirname(__file__), 'templates')
jinja_env = jinja2.Environment(loader = jinja2.FileSystemLoader(template_dir),
                               autoescape = True)

secret = 'fart'

def render_str(template, **params):
    t = jinja_env.get_template(template)
    return t.render(params)

def make_secure_val(val):
    return '%s|%s' % (val, hmac.new(secret, val).hexdigest())

def check_secure_val(secure_val):
    val = secure_val.split('|')[0]
    if secure_val == make_secure_val(val):
        return val

class Handler(webapp2.RequestHandler):
    def write(self, *a, **kw):
        self.response.out.write(*a, **kw)

    def render_str(self, template, **params):
        params['user'] = self.user
        return render_str(template, **params)

    def render(self, template, **kw):
        self.write(self.render_str(template, **kw))

    def set_secure_cookie(self, name, val):
        cookie_val = make_secure_val(val)
        self.response.headers.add_header(
            'Set-Cookie',
            '%s=%s; Path=/' % (name, cookie_val))

    def read_secure_cookie(self, name):
        cookie_val = self.request.cookies.get(name)
        return cookie_val and check_secure_val(cookie_val)

    def login(self, user):
        self.set_secure_cookie('user_id', str(user.key().id()))

    def logout(self):
        self.response.headers.add_header('Set-Cookie', 'user_id=; Path=/')

    def initialize(self, *a, **kw):
        webapp2.RequestHandler.initialize(self, *a, **kw)
        uid = self.read_secure_cookie('user_id')
        self.user = uid and User.by_id(int(uid))

class MainPage(Handler):
    def get(self):
        self.render("home.html")

##### user stuff
def make_salt(length = 5):
    return ''.join(random.choice(letters) for x in xrange(length))

def make_pw_hash(name, pw, salt = None):
    if not salt:
        salt = make_salt()
    h = hashlib.sha256(name + pw + salt).hexdigest()
    return '%s,%s' % (salt, h)

def valid_pw(name, password, h):
    salt = h.split(',')[0]
    return h == make_pw_hash(name, password, salt)

def users_key(group = 'default'):
    return ndb.Key.from_path('users', group)

class Images(ndb.Model):
    file_name = ndb.StringProperty()
    blob = ndb.BlobProperty()

class User(ndb.Model):
    name = ndb.StringProperty(required = True)
    pw_hash = ndb.StringProperty(required = True)
    email = ndb.StringProperty()

    @classmethod
    def by_id(cls, uid):
        return User.get_by_id(uid, parent = users_key())

    @classmethod
    def by_name(cls, name):
        u = User.all().filter('name =', name).get()
        return u

    @classmethod
    def register(cls, name, pw, email = None):
        pw_hash = make_pw_hash(name, pw)
        return User(parent = users_key(),
                    name = name,
                    pw_hash = pw_hash,
                    email = email)

    @classmethod
    def login(cls, name, pw):
        u = cls.by_name(name)
        if u and valid_pw(name, pw, u.pw_hash):
            return u


USER_RE = re.compile(r"^[a-zA-Z0-9_-]{3,20}$")
def valid_username(username):
    return username and USER_RE.match(username)

PASS_RE = re.compile(r"^.{3,20}$")
def valid_password(password):
    return password and PASS_RE.match(password)

EMAIL_RE  = re.compile(r'^[\S]+@[\S]+\.[\S]+$')
def valid_email(email):
    return not email or EMAIL_RE.match(email)

class Signup(Handler):
    def get(self):
        self.render("signup-form.html")

    def post(self):
        have_error = False
        self.username = self.request.get('username')
        self.password = self.request.get('password')
        self.verify = self.request.get('verify')
        self.email = self.request.get('email')

        params = dict(username = self.username,
                      email = self.email)

        if not valid_username(self.username):
            params['error_username'] = "That's not a valid username."
            have_error = True

        if not valid_password(self.password):
            params['error_password'] = "That wasn't a valid password."
            have_error = True
        elif self.password != self.verify:
            params['error_verify'] = "Your passwords didn't match."
            have_error = True

        if not valid_email(self.email):
            params['error_email'] = "That's not a valid email."
            have_error = True

        if have_error:
            self.render('signup-form.html', **params)
        else:
            self.done()

    def done(self, *a, **kw):
        raise NotImplementedError

class Register(Signup):
    def done(self):
        #make sure the user doesn't already exist
        u = User.by_name(self.username)
        if u:
            msg = 'That user already exists.'
            self.render('signup-form.html', error_username = msg)
        else:
            u = User.register(self.username, self.password, self.email)
            u.put()

            self.login(u)
            self.redirect('/')

class Login(Handler):
    def get(self):
        self.render('login-form.html')

    def post(self):
        username = self.request.get('username')
        password = self.request.get('password')

        u = User.login(username, password)
        if u:
            self.login(u)
            frontuser = username
            self.render('home.html', frontuser = frontuser)
        else:
            msg = 'Invalid login'
            self.render('login-form.html', error = msg)

class Logout(Handler):
    def get(self):
        self.logout()
        self.redirect('/')

class MP(Handler):
    def get(self):
        self.render('mp.html')

class FileUpload(Handler):
    def post(self):
        file_upload = self.request.POST.get("file", None)
        file_name = file_upload.filename
        image = Images(id=file_name, file_name=file_name, blob=file_upload.file.read())
        image.put()

        self.response.headers[b'Content-Type'] = mimetypes.guess_type(image.file_name)[0]
        self.response.write(image.blob)

class ImgServe(Handler):
    def get(self, resource):
        image = ndb.Key('Images', resource).get()
        self.response.headers[b'Content-Type'] = mimetypes.guess_type(image.file_name)[0]
        self.response.write(image.blob)            

class Blob(Handler):
    def get(self):
        self.render("blob.html")

class Template(Handler):
    def get(self):
        self.render('template.html')


app = webapp2.WSGIApplication([('/', MainPage),
                                ('/signup', Register),
                                ('/login', Login),
                                ('/logout', Logout),
                                ('/mp', MP),
                                (r'/file_upload', FileUpload),
                                ('/blob', Blob),
                                (r'/img_serve/<resource:(.*)>', ImgServe),
                                ('/template', Template)], debug=True)

blob.html

<!DOCTYPE HTML>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Image Upload</title>
</head>
<body>
<form action="/file_upload" enctype="multipart/form-data" method="post">
    <div><input type="file" name="file"/></div>
    <div><input type="submit" value="Upload"></div>
</form>
</body>
</html>

template.html

<html>
<head>
    <title>Template</title>
</head>
<body>
    <img src="/img_serve/database.jpeg" /> 
</body>
</html>

Also, here's what I get instead of an image:

enter image description here

Manas Chaturvedi
  • 5,210
  • 18
  • 52
  • 104
  • Have you tried fetching the image directly. Are you getting a 404, or getting data but not interpreted as an image ? – Tim Hoffman Mar 09 '14 at 08:45
  • 1
    Also how are you creating the images. You datastore viewer shows the keys are with auto generated id's so the numeric id is the "resource" value, . You should probably create the image with "database.jpg" as the key if you want to retrieve it with the code you have shown. – Tim Hoffman Mar 09 '14 at 08:48
  • I have included some more information in my original post which may answer your questions above. Also, by using the numeric id as the resource value, the problem doesn't seem to go away. Here's what I did: where that numeric value is the ID of the 'database.jpeg'. – Manas Chaturvedi Mar 09 '14 at 09:27
  • 1
    In your .py code: Is the image displayed after it was uploaded? In this code the file_name is used for the id. – voscausa Mar 09 '14 at 13:40
  • 1
    Your code above does not match the DataViewer. In the screen shot you have numeric id's However you code is explicitly setting the id to the filename. It has to be one or the other. If your dataviewer is showing numeric id's then there is an issue with your code or the screen shot is out of date. – Tim Hoffman Mar 09 '14 at 14:09
  • I have edited my original code and included the whole code of my application. I have images already stored in the datastore. 'database.jpeg' was already stored manually by me in the datastore, and I'm trying to display it in my application using template.html. Hope that clears all the misunderstanding. – Manas Chaturvedi Mar 09 '14 at 14:23
  • If you store it manualy, you cannot give it a key yourself! And if you use a dadastore key, it is better to use a safe key in your HTML. See NDB: image.key.urlsafe() – voscausa Mar 09 '14 at 14:44
  • I have updated the screenshot for my entity 'Images' in my original post. Sorry for all the misunderstanding that was caused because of my carelessness! Now that being said, how can I extract the image from the datastore? – Manas Chaturvedi Mar 09 '14 at 15:46
  • In the ImgServe class, even after using the key_name of the entity and displaying 'image.blob', I still get a broken image as before instead of the actual image. What's happening? Is there something I'm missing in my code? – Manas Chaturvedi Mar 10 '14 at 18:19

2 Answers2

0

Using App engine SDK 1.9.0 and Python 27 the SDK datastore should look like this after uploading an image with your upload code and file_name: clock.png.enter image description here

In your code : class FileUpload(Handler) and class ImgServe(Handler):

But why not use webapp2.RequestHandler for Handler?

Using this static HTML:

<!DOCTYPE HTML>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Image Serving</title>
</head>
<body>
   <img src="/img_serve/clock.png" alt="clock" >
</body>
</html>

And this handler:

class ImgServe(webapp2.RequestHandler):

    def get(self, resource):
        image = ndb.Key('Images', resource).get()
        self.response.headers[b'Content-Type'] = mimetypes.guess_type(image.file_name)[0]
        self.response.write(image.blob)

Routing:

    webapp2.Route(r'/img_serve/<resource:(.*)>', handler=....ImgServe)
voscausa
  • 11,253
  • 2
  • 39
  • 67
  • I already have...this is how the class Handler is defined in my code: class Handler(webapp2.RequestHandler). I've included the whole code in my original post now. – Manas Chaturvedi Mar 09 '14 at 15:50
0

I would recommend that you use a Blobstore for storing images - it's cheaper and it's specifically optimized for this purpose.

Better yet, you can store images in Google Cloud Storage. It's even cheaper than Blobstore.

Both solutions offer an additional benefit - browsers will be able to cache these images.

Andrei Volgin
  • 40,755
  • 6
  • 49
  • 58
  • I agree. But 1) Google is moving away from the blovstore 2) The Google Cloud Storage implementation for FREE app engine applications is not finished yet 3) For small images and other kinds of blobs his code can work, but is very from ideal for images. – voscausa Mar 09 '14 at 14:41
  • You can use GCS with any app, free or not. – Andrei Volgin Mar 09 '14 at 14:50
  • Yes, but with all kind of problems, like using it in the SDK (local clodstorage) and making it public using : options={b'x-goog-acl': b'public-read'} does not work. – voscausa Mar 09 '14 at 14:55
  • I did not have these problems. – Andrei Volgin Mar 09 '14 at 15:04
  • ?? What about this one : http://stackoverflow.com/questions/22174903/how-to-serve-cloudstorage-files-using-app-engine-sdk – voscausa Mar 09 '14 at 15:31