English ▼
Skip to page content Skip to main navigation
e.g. "templates" or "datastore"

# Serving Dynamic Images with Google App Engine (Java)

Austin Chau, Jason Cooper
April 2008, updated March 2010 for Java

A picture is worth a thousand words. This is definitely true with today's web applications where images play a big part in reaching the audience. App Engine allows you to store and serve images quickly and easily through its datastore. In this short article, I will walk you through how to store and serve images in App Engine's datastore using code snippets for storing and retrieving movie information.

## Storing Images in the Datastore

For starters, we would like to store each movie in the application as an object of the datastore. This is accomplished by defining a simple Java class that models a movie. For this project, we will use Java Data Objects (JDO), a standard interface for storing Java data objects in a persistent database. A Java Persistence API (JPA) wrapper is also available, or you can use the low-level API.

The `Movie` class below defines all the attributes a `Movie` object would have. It's a standard Java class peppered with JDO annotations to describe how the various members should be persisted. (For additional background on these annotations, see Using JDO.) One of the fields, image, will contain the actual bytes that make up the movie's thumbnail image. Therefore, we need to declare the image as a Blob, which you can think of as a large array of bytes.

Note: Do not confuse this with the Blobstore API which can also be used to store large data objects such as images but does not do so via the datastore. This article uses the datastore exclusively.

    package com.google.appengine.demo.domain;

    import com.google.appengine.api.datastore.Blob;
    import com.google.appengine.api.datastore.Key;

    import javax.jdo.annotations.Extension;
    import javax.jdo.annotations.IdGeneratorStrategy;
    import javax.jdo.annotations.IdentityType;
    import javax.jdo.annotations.PersistenceCapable;
    import javax.jdo.annotations.Persistent;
    import javax.jdo.annotations.PrimaryKey;

    /**
     * JDO-annotated model class for storing movie properties; movie's promotional
     * image is stored as a Blob (large byte array) in the image field.
     */
    @PersistenceCapable(identityType = IdentityType.APPLICATION)
    public class Movie {

        @PrimaryKey
        @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
        private Key key;

        @Persistent
        private String title;

        @Persistent
        @Extension(vendorName="datanucleus", key="gae.unindexed", value="true")
        private String imageType;

        @Persistent
        private Blob image;

        //...

        public Long getId() {
            return key.getId();
        }

        public String getTitle() {
            return title;
        }

        public String getImageType() {
            return imageType;
        }

        public byte[] getImage() {
            if (image == null) {
                return null;
            }

            return image.getBytes();
        }

        public void setTitle(String title) {
            this.title = title;
        }

        public void setImageType(String imageType) {
            this.imageType = imageType;
        }

        public void setImage(byte[] bytes) {
            this.image = new Blob(bytes);
        }

        //...
    }

Now that we have the model defined, we can start using it to store movie objects. We can get movie data from a remote source and feed it into the application. To do this, we'll use App Engine's URL Fetch API which can retrieve data from HTTP and HTTPS URLs. Although App Engine for Java includes a familiar `java.net` wrapper on top of the URL Fetch service, we'll use the low-level API below. Just specify or otherwise retrieve a reference to the image URL, then pass it into a `URLFetchService` object's `fetch` method to fetch the binary content of a remote image.

Next, create a new `Movie` object and pass the fetched bytes into `setImage`, which creates a new `Blob` object. We'll also store the MIME type of the image (e.g. image/jpg, image/png, etc.) which should be returned in the 'Content-Type' header. `makePersistent` is then called to commit the change to the datastore.

    package com.google.appengine.demo.web;

    import com.google.appengine.api.urlfetch.HTTPHeader;
    import com.google.appengine.api.urlfetch.HTTPResponse;
    import com.google.appengine.api.urlfetch.URLFetchService;
    import com.google.appengine.api.urlfetch.URLFetchServiceFactory;
    import com.google.appengine.demo.domain.Movie;
    import com.google.appengine.demo.repo.PMF;

    import java.io.IOException;
    import java.net.URL;

    import javax.jdo.PersistenceManager;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    /**
     * GET requests fetch the image at the URL specified by the url query string
     * parameter, then persist this image along with the title specified by the
     * title query string parameter as a new Movie object in App Engine's
     * datastore.
     */
    public class StoreMovieServlet extends HttpServlet {

        @Override
        public void doGet(HttpServletRequest req, HttpServletResponse resp)
                throws IOException {
            URLFetchService fetchService =
                URLFetchServiceFactory.getURLFetchService();

            // Fetch the image at the location given by the url query string parameter
            HTTPResponse fetchResponse = fetchService.fetch(new URL(
                    req.getParameter("url")));

            String fetchResponseContentType = null;
            for (HTTPHeader header : fetchResponse.getHeaders()) {
                // For each request header, check whether the name equals
                // "Content-Type"; if so, store the value of this header
                // in a member variable
                if (header.getName().equalsIgnoreCase("content-type")) {
                    fetchResponseContentType = header.getValue();
                    break;
                }
            }

            if (fetchResponseContentType != null) {
                // Create a new Movie instance
                Movie movie = new Movie();
                movie.setTitle(req.getParameter("title"));
                movie.setImageType(fetchResponseContentType);

                // Set the movie's promotional image by passing in the bytes pulled
                // from the image fetched via the URL Fetch service
                movie.setImage(fetchResponse.getContent());

                //...

                PersistenceManager pm = PMF.get().getPersistenceManager();
                try {
                    // Store the image in App Engine's datastore
                    pm.makePersistent(movie);
                } finally {
                    pm.close();
                }
            }
        }
    }

## Retrieving & Displaying Images

Now that the objects are populated in our `Movie` datastore, let's set up a request handler to retrieve them dynamically from the datastore and render the objects back to the browser as images.

First, we create a new servlet, `GetImageServlet`, to define the GET request handler. The first thing this request handler does is retrieve the value of 'title' from the URL parameter. Then it retrieves the `Movie` object that matches the title from the datastore by calling `getMovie`, which is defined a bit later.

Once we have the reference to the `Movie` object, we can access its image field. In order for the browser to properly render the image, we set our response's HTTP `Content-Type` header to the value we extracted earlier when originally storing the `Movie` object.

    package com.google.appengine.demo.web;

    import com.google.appengine.demo.domain.Movie;
    import com.google.appengine.demo.repo.PMF;

    import java.io.IOException;
    import java.util.List;

    import javax.jdo.PersistenceManager;
    import javax.jdo.Query;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    /**
     * GET requests return the promotional image associated with the movie with the
     * title specified by the title query string parameter.
     */
    public class GetImageServlet extends HttpServlet {

        @Override
        public void doGet(HttpServletRequest req, HttpServletResponse resp)
                throws IOException {
            String title = req.getParameter("title");
            Movie movie = getMovie(title);

            if (movie != null && movie.getImageType() != null &&
                    movie.getImage() != null) {
                // Set the appropriate Content-Type header and write the raw bytes
                // to the response's output stream
                resp.setContentType(movie.getImageType());
                resp.getOutputStream().write(movie.getImage());
            } else {
                // If no image is found with the given title, redirect the user to
                // a static image
                resp.sendRedirect("/static/noimage.jpg");
            }
        }

        //...

The `getMovie` method uses JDO's query API to retrieve a `Movie` object that matches the title.

    /**
     * Queries the datastore for the Movie object with the passed-in title. If
     * found, returns the Movie object; otherwise, returns null.
     *
     * @param title movie title to look up
     */
    private Movie getMovie(String title) {
        PersistenceManager pm = PMF.get().getPersistenceManager();

        // Search for any Movie object with the passed-in title; limit the number
        // of results returned to 1 since there should be at most one movie with
        // a given title
        Query query = pm.newQuery(Movie.class, "title == titleParam");
        query.declareParameters("String titleParam");
        query.setRange(0, 1);

        try {
            List<Movie> results = (List<Movie>) query.execute(title);
            if (results.iterator().hasNext()) {
                // If the results list is non-empty, return the first (and only)
                // result
                return results.get(0);
            }
        } finally {
            query.closeAll();
            pm.close();
        }

        return null;
    }

To make it easy to access the images of the movies in the application, we can map the `GetImageServlet` class to the URL path `'/image'`. In Java, we do this via the standard `web.xml` deployment descriptor file:

    <?xml version="1.0" encoding="utf-8"?>
    <web-app
      xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
      version="2.5">
      <servlet>
        <servlet-name>StoreMovie</servlet-name>
        <servlet-class>com.google.appengine.demo.web.StoreMovieServlet</servlet-class>
      </servlet>
      <servlet>
        <servlet-name>GetImage</servlet-name>
        <servlet-class>com.google.appengine.demo.web.GetImageServlet</servlet-class>
      </servlet>
      <servlet-mapping>
        <servlet-name>StoreMovie</servlet-name>
        <url-pattern>/addMovie</url-pattern>
      </servlet-mapping>
      <servlet-mapping>
        <servlet-name>GetImage</servlet-name>
        <url-pattern>/image</url-pattern>
      </servlet-mapping>
    </web-app>

Now when a visitor visits the URL - http://mydomain.com/image?title=matrix, an image for the movie "matrix" is returned to the browser.

## Resources

We have just shown you how you can easily serve images from your Google App Engine's datastore. Now it's time for you to get your hands dirty with code! Check out the resources below to get started on building your next great app:

- Datastore API Reference
- URL Fetch API Reference