Uploading and Re-Sizing Large Images on App Engine

Posted on : 2010-11-15 | By : Sam Edwards | In : APIs, App Engine, Tips, Tools

Tags: , , ,

2

You may be under the impression that manipulating images larger than 1mb is not be possible on Google’s App Engine. This is not true though (sorta). Because most digital cameras produce images over 1mb in size, and requiring users to resize images before uploading is so 1990, it seems like the handling of large images would be an available feature. If you read the App Engine documentation on Image Manipulation, you may be under the impression, just as many are that you cannot use their image manipulation utilities on files over 1mb in size.

How To:
While it is true that the Image Manipulation Service cannot be used directly with image data over 1mb in size, it can be if it gets the image data from the App Engine Blobstore.  I got pointed in the direction of the Blobstore from this post from Developing in the Dark. By using the Blobstore, it is possible to use their Image manipulation API for files over 1mb in size.

Here is an example in Java and JSP that will allow the uploading, serving, and use of App Engine’s Image Manipulation service. This example is a modified version of the examples found in the App Engine Documentation. (Blobstore Example). (Image Manipulation with the Blobstore Example).

<%@ page import="com.google.appengine.api.blobstore.BlobstoreServiceFactory" %>
<%@ page import="com.google.appengine.api.blobstore.BlobstoreService" %>

<%
    BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
%>

<html>
    <head>
        <title>Upload Test</title>
    </head>
    <body>
        <form action="<%= blobstoreService.createUploadUrl("/upload") %>" method="post" enctype="multipart/form-data">
            <input type="text" name="foo">
            <input type="file" name="myFile">
            <input type="submit" value="Submit">
        </form>
    </body>
</html>
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns="http://java.sun.com/xml/ns/javaee"
   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>Upload</servlet-name>
    <servlet-class>Upload</servlet-class>
  </servlet>

  <servlet>
    <servlet-name>Serve</servlet-name>
    <servlet-class>Serve</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>Upload</servlet-name>
    <url-pattern>/upload</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>Serve</servlet-name>
    <url-pattern>/serve</url-pattern>
  </servlet-mapping>

</web-app>
import java.io.IOException;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;

public class Upload extends HttpServlet {
    private BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();

    public void doPost(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {

        Map blobs = blobstoreService.getUploadedBlobs(req);
        BlobKey blobKey = blobs.get("myFile");

        if (blobKey == null) {
            res.sendRedirect("/");
        } else {
            res.sendRedirect("/serve?blob-key=" + blobKey.getKeyString());
        }
    }
}
import java.io.IOException;

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

import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;

public class Serve extends HttpServlet {
    private BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();

public void doGet(HttpServletRequest req, HttpServletResponse res)
    throws IOException {
        BlobKey blobKey = new BlobKey(req.getParameter("blob-key"));
        blobstoreService.serve(blobKey, res);
    }
}
import java.io.IOException;
import java.io.OutputStream;

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

import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
import com.google.appengine.api.images.Image;
import com.google.appengine.api.images.Image.Format;
import com.google.appengine.api.images.ImagesService;
import com.google.appengine.api.images.ImagesServiceFactory;
import com.google.appengine.api.images.Transform;

public class Resize extends HttpServlet {

	/**
	 * Default Serialization UID
	 */
	private static final long serialVersionUID = 1L;

	private BlobstoreService blobstoreService = BlobstoreServiceFactory
			.getBlobstoreService();

	public void doGet(HttpServletRequest req, HttpServletResponse res)
			throws IOException {
		//Get the Blob Key and grab the Image from the Blobstore
		BlobKey blobKey = new BlobKey(req.getParameter("blob-key"));
        Image oldImage = ImagesServiceFactory.makeImageFromBlob(blobKey);	

		//Create the Image Service Factory and the Resize Transform
		ImagesService imagesService = ImagesServiceFactory.getImagesService();
        Transform resize = ImagesServiceFactory.makeResize(300, 200);

        //Resize The Image using the transform created above
        Image resizedImage = imagesService.applyTransform(resize, oldImage);

        //Serve the newly transformed image
        byte[] resizedImageData = resizedImage.getImageData();
        OutputStream outputStream = res.getOutputStream();
        outputStream.write(resizedImageData);
	}
}

Comments (2)

While this is a great service, we have run into problems using it externally to manage images. It doesn’t seem possible to POST an image to the service externally. The only way to upload is from an application hosted on App Engine.

Any ideas on if this is possible to post images from another site?

Thanks

Hey Jeff, this is possible. You’d just need to make an extra request to AppEngine before uploading to get the upload url. More info on this JSP page here. http://code.google.com/appengine/docs/java/blobstore/overview.html#Uploading_a_Blob

I managed to do this in an android application too, you’d just have to have an endpoint on your server return you a new upload url.

-Sam Edwards
@HandstandSam

Write a comment