开发者

Clear example of using Google App Engine Images get_serving_url()

开发者 https://www.devze.com 2023-03-16 21:21 出处:网络
Anybody k开发者_如何学Cnow of an example of this? I haven\'t been able to find one in Google\'s documentation.Despite the documentation, I was pretty confused for a while too.

Anybody k开发者_如何学Cnow of an example of this? I haven't been able to find one in Google's documentation.


Despite the documentation, I was pretty confused for a while too.

Now that I understand it better (I think!), I'll offer an example.

get_serving_url() is in the google.appengine.ext.blobstore class, and takes one positional argument, the BlobKey.

A BlobKey can be constructed from a string: blobstore.BlobKey('this is the key').

So, that gives us everything we need for a basic implementation of get_serving_url():

from google.appengine.ext.blobstore import BlobKey
from google.appengine.api.images import get_serving_url

key = BlobKey('imagekey')
url = get_serving_url(key)

All fine and dandy so far.

The function also takes three keyword arguments, as documented. These are size, crop, secure_url, and rpc.

  • secure_url = True simply returns an https url instead of http (default, False)
  • rpc is an RPC object of some settings for asynchronous processing. I don't understand it enough to explain, or indeed to use it myself!
  • crop = True crops the image square, by even proportions.

size is what confused me at first. It does not generate different URLs per se. The only difference is in the suffix =sXX, which you are free to set yourself.

Personally, I store the original size URL in my db.Model, and then do imgUrl+'=s150' (for example) wherever used. There is no need to call get_serving_url() for each different size you need, no performance hit, because it is doing exactly the same.

Note also that the size specified is the largest dimension of the image. This is curiously hidden in the docs - I assumed it must be width for a while, but if the image is 'portrait', of course it is the height.

You can also append -c (equivalent to crop=True).

So, for our more complete (though I lack the knowledge to demonstrate use of an RPC object) example:

from google.appengine.ext.blobstore import BlobKey
from google.appengine.api.images import get_serving_url
from webapp2 import RequestHandler

class sample(RequestHandler):

    def get(self):
        key = BlobKey('imagekey')
        url = get_serving_url(key, secure_url=True)

        #get_serving_url(key, secure_url=True, size=150, crop=True)
        urlThumb = url + '=s150-c'

        #get_serving_url(key, secure_url=True, size=50)
        urlMini  = url + '=s50'

        self.response.write('Here is my thumbnail: <img src="%s"><br>'%urlThumb)
        self.response.write('Here is mini-me!: <img src="%s"><br>'%urlMini)
        self.response.write('And back to full-size: <img src="%s"><br>'%url)

Those URL's can then be stored in the Datastore in whichever model they're relevant to. This is preferred over using the completely different db.BlobProperty, which is not really for images at all. It's also more expensive, and less efficient.

Of course, I would suggest you store only url (as above), because it's so easy to change the size by suffixing the string! In fact, it's something you can really just do in your Jinja template (or equivalent) - where you would probably otherwise specify width= and crop by doing the same in CSS.


get_serving_url is documented here. There's no end-to-end example per-se, but it's pretty straightforward: You pass it a blob key, along with optional resize and crop options, and it gives you back a URL. You can use that URL anywhere you want to reference the image, and it'll be served up by the infrastructure, resized and cropped appropriately.

Note that this is only for images uploaded to the blobstore. Anything uploaded to the regular datastore and stored in a BlobProperty you'll have to resize, crop, and serve yourself.


I have been using the following code to upload images and then serve them using the CDN. Please refer to the comments in the code for the explanation.

import webapp2
from google.appengine.api import users
import os
import jinja2
from models import Note
from models import NoteFile
from models import CheckListItem
from google.appengine.ext import ndb
from google.appengine.api import app_identity
from google.appengine.api import images
from google.appengine.ext import blobstore
import lib.cloudstorage as cloudstorage
import mimetypes

jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))

#Request Handler
class MainHandler(webapp2.RequestHandler):  
    #Post Method for uploading images  
    def post(self):
        #Gets the currently logged in user
        user = users.get_current_user() 
        if user is None:
            self.error(401)
        #Gets Default Bucket for Google Cloud Storage. This is where uploaded image will be saved
        bucket_name = app_identity.get_default_gcs_bucket_name()
        uploaded_file = self.request.POST.get('uploaded_file')
        file_name = getattr(uploaded_file, 'filename', None)
        file_content = getattr(uploaded_file, 'file', None)
        real_path = ''

        if file_name and file_content:
            content_t = mimetypes.guess_type(file_name)[0]
            real_path = os.path.join('/', bucket_name, user.user_id(), file_name)
            #Read file from the uploaded stream and write to the cloud storage bucket
            with cloudstorage.open(real_path, 'w', content_type=content_t) as f:
                f.write(file_content.read())
        else:
            print 'File can not be written'
        #This will save the NDB models
        self._create_note(user, file_name, real_path)

        logout_url = users.create_logout_url(self.request.uri)
        template_context = {
            'user': user.nickname(),
            'logout_url': logout_url
        }
        #Response with the data
        self.response.write(self._render_template('main.html', template_context))

    #Makes the method atomic
    @ndb.transactional
    def _create_note(self, user, file_name, file_path):
        note = Note(parent=ndb.Key("User", user.nickname()),
                    title=self.request.get('title'),
                    content=self.request.get('content'))
        note.put()

        if file_name and file_path:
            url, thumbnail_url = self._get_urls_for(file_name)
            f = NoteFile(parent=note.key, name=file_name, url=url, thumbnail_url=thumbnail_url, full_path=file_path)
            f.put()
            note.files.append(f.key)
        note.put()

    def _render_template(self, template_name, context=None):
        if context is None:
            context = {}
        user = users.get_current_user()
        ancestor_key = ndb.Key("User", user.nickname())
        qry = Note.owner_query(ancestor_key)
        context['notes'] = qry.fetch()
        template = jinja_env.get_template(template_name)
        return template.render(context)

    def _get_urls_for(self, file_name):
        user = users.get_current_user()
        if user is None:
            return
        #Gets Default Bucket
        bucket_name = app_identity.get_default_gcs_bucket_name()
        path = os.path.join('/', bucket_name, user.user_id(), file_name)
        #This is required to generate the blobstore key
        real_path = '/gs' + path
        key = blobstore.create_gs_key(real_path)
        #This is going to generate url for original sized image
        url = images.get_serving_url(key, size=0)
        #Generates url for cropped and 150px max dimension image. The image will be uploaded once, but will dynamically be transformed acc to parameters provided
        thumbnail_url = images.get_serving_url(key, size=150, crop=True)
        return url, thumbnail_url


app = webapp2.WSGIApplication([
    (r'/', MainHandler)
], debug=True)

Here're the model classes

class Note(ndb.Model):
    title = ndb.StringProperty()
    content = ndb.TextProperty()
    date_created = ndb.DateTimeProperty(auto_now_add=True)
    files = ndb.KeyProperty("NoteFile",repeated=True)

    @classmethod
    def owner_query(cls, parent_key):
        return cls.query(ancestor=parent_key).order(-cls.date_created)


class NoteFile(ndb.Model):
    name = ndb.StringProperty()
    url = ndb.StringProperty()
    thumbnail_url = ndb.StringProperty()
    full_path = ndb.StringProperty()

Let me know if anything is not clear here.


I was also highly confused as google doesn't really provide a working example of the getServingUrl() for JAVA programmers - Ollie, I think the example code you have given above is in Python?

I did some coding to see how it works and here is a working code snippet in java (one can easily take the working example from google's site: https://cloud.google.com/appengine/docs/java/blobstore/ and replace the code written under the Serve.java with this piece of code):

   @Override
    public void doGet(HttpServletRequest req, HttpServletResponse res)
        throws IOException {
            BlobKey blobKey = new BlobKey(req.getParameter("blob-key"));

            ImagesService imagesService = ImagesServiceFactory.getImagesService();

            String url = imagesService.getServingUrl(ServingUrlOptions.Builder.withBlobKey(blobKey).crop(true).imageSize(200));
            PrintWriter out = res.getWriter();
            out.println("Here is my thumbnail! <img src="+ url + ">");

        }
}

This will take the image you have post to your blobstore, crop it so that it becomes a nice square with 200 as its width and height and then print it out in HTML so that you can see the thumbnail.

Hope this helps someone out there!

0

精彩评论

暂无评论...
验证码 换一张
取 消