Please note that the contents of this offline web site may be out of date. To access the most recent documentation visit the online version .
Note that links that point to online resources are green in color and will open in a new window.
We would love it if you could give us feedback about this material by filling this form (You have to be online to fill it)



Serving Photos

For applications using Google Cloud platform, a common pattern of data storage and retrieval is the storing of binary data in Google Cloud Storage (GCS) and data related to the binary data, such as keys, owner, timestamp, etc., in other storage, such as App Engine Datastore or Google Cloud SQL.

The Photofeed sample illustrates this pattern in its storage of photo image binaries in GCS and the storage of the corresponding photo metadata in the Datastore as Photo entities, with the metadata including the keys needed to serve the photo image binaries.

Synopsis

In the Photofeed sample, when the main JSP page ( war/photofeed.jsp ) loads, all of the photos are displayed in the order uploaded, with all comments for each photo displayed under the photo, in the order the comments were uploaded.

To understand what is happening in the photo and comment iteration, we need to remember that the Comment objects and the Photo "metadata" objects are stored in datastore while the actual photo binaries are stored elsewhere, in Google Cloud Storage. So, we iterate through each Photo object to get the blob key we need to serve the corresponding photo binary from Google Cloud Storage, using the blobstoreService .

The list of Photo objects is retrieved from the datastore and is iterated. In each iteration, the photo ID and photo owner ID are used by the PhotoServiceManager in a GET request to the DownloadServlet running at /download . This servlet gets the blob key from the Photo object being iterated, and supplies this blob key to the blobstoreService , which serves the photo binary.

After the binary is served, all of the Comment objects for the Photo are displayed.

Let's take a closer look at the code.

Serving Photos in the Photofeed JSP

The following code in /photo-sharing-demo/war/photofeed.jsp shows the start of the iteration through the Photo objects stored in the Datastore:

 <%
      Iterable<Photo> photoIter = photoManager.getActivePhotos();
      ArrayList<Photo> photos = new ArrayList<Photo>();
      for (Photo photo : photoIter) {
        photos.add(photo);
      }
      int count = 0;
      for (Photo photo : photos) {
        String firstClass = "";
        String lastClass = "";
        if (count == 0) {
          firstClass = "first";
        }
        if (count == photos.size() - 1) {
          lastClass = "last";
        }
    %>
   <div class="feed<%= firstClass %><%= lastClass %>">
     <div class="post group">
       <div class="image-wrap">
         <img class="photo-image"
            src="<%= serviceManager.getImageDownloadUrl(photo)%>"
            alt="Photo Image" />
       </div>
       <div class="owner group">
         <img src="<%= ServletUtils.getUserIconImageUrl(photo.getOwnerId()) %>"
            alt="" />
         <div class="desc">
           <h3><%= ServletUtils.getProtectedUserNickname(photo.getOwnerNickname()) %></h3>
           <p><%= photo.getTitle() %>
           <p>
           <p class="timestamp"><%= ServletUtils.formatTimestamp(photo.getUploadTime()) %></p>
         </div>
         <!-- /.desc -->
       </div>
       <!-- /.usr -->
     </div>

In the code above, the PhotoManager interface is used to get a list of all active Photo objects from the datastore. For more details about active versus inactive Photos see " How to do "Transactional Deletes" across Storages .

The list of Photos is iterated, with each iteration resulting in the serving and display of the photo image from Google Cloud Storage along with the display of the related user icon, name, and timestamp obtained directly from the datastore Photo being iterated.

The serving of the image begins here:

serviceManager.getImageDownloadUrl(photo)

If you look at the definition of this method in PhotoServiceManager.java you'll see that it builds a GET request that includes the photo ID and the photo owner ID and sends it to the URL /download . Taking a look at war/WEB-INF/web.xml , the servlet that handles requests at that URL is DownloadServlet.java :

public class DownloadServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
    String user = req.getParameter("user");
    String id = req.getParameter("id");
    Long photoId = ServletUtils.validatePhotoId(id);
    if (photoId != null && user != null) {
      Photo photo = AppContext.getAppContext().getPhotoManager().getPhoto(user, photoId);
      BlobKey blobKey = photo.getBlobKey();
      BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
      blobstoreService.serve(blobKey, res);
    } else {
      res.sendError(400, "One or more parameters are not set");
    }
  }
}

The servlet code shows how to serve a blob in Google Cloud Storage using blobstoreService . The blob key is extracted from the Datastore object containing the metadata for the blob, and the blob key is used to get the image binary and serve it in the response.

Continuing the Iteration: Comments

During the iteration of each photo, after the photo image is served, the comments for the photo are retrieved and iterated:

 Iterable<Comment> comments = commentManager.getComments(photo);
        for (Comment comment : comments) {
    %>
      <div class="post group">
        <div class="usr">
          <img
            src="<%= ServletUtils.getUserIconImageUrl(comment.getCommentOwnerId()) %>"
            alt="" />
          <div class="comment">
            <h3><%= ServletUtils.getProtectedUserNickname(comment.getCommentOwnerName()) %></h3>
            <p><%= comment.getContent() %>
            <p>
            <p class="timestamp"><%= ServletUtils.formatTimestamp(comment.getTimestamp()) %></p>
          </div>
          <!-- /.comment -->
        </div>
        <!-- /.usr -->
      </div>
      <!-- /.post -->

      <%
        }
    %>

In this code, the CommentManager model interface returns a list of all comments for the photo currently being iterated. Those comments are iterated and displayed along with details such the comment author, user icon, and timestamp.

Authentication required

You need to be signed in with Google+ to do that.

Signing you in...

Google Developers needs your permission to do that.