Uploading And Serving Files With The Blobstore In Java

Here’s a code example to let users upload a file to the Blobstore and then download it back again. This code uses a servlet and a JSP page: the JSP page shows a form and the servlet accepts the upload, then immediately sends the uploaded file back to the user.

Here’s the doPost and doGet code for the servlet (in this example, we aliased the servlet to /blobstoreexample):

public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws IOException {
    String blob_key_string = req.getParameter("blob_key");
    System.out.println("BLOBKEY: " + blob_key_string);
    if (blob_key_string == null) {
        resp.sendRedirect("/example.jsp");
        System.out.println("No blobkey given, redirecting to upload page.");
    }
    else {
        BlobKey blob_key = new BlobKey(blob_key_string);
        BlobstoreServiceFactory.getBlobstoreService().serve(blob_key, resp);
        System.out.println("Blobkey given, sending file.");
    }
}//end doGet


public void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws IOException {
    try {
        Map<String, List<BlobKey>> files_sent = BlobstoreServiceFactory.getBlobstoreService().getUploads(req);
        BlobKey file_uploaded_key = files_sent.get("file").get(0);
        resp.sendRedirect("/blobstoreexample?blob_key=" + file_uploaded_key.getKeyString());
        System.out.println("Document successfully POSTED, redirect to doGET");
    }
    catch (Exception e) {
        resp.sendRedirect("/example.jsp");
        System.out.println("Document failed to POST, redirecting back to upload.");
    }
}//end doPost

Here is the code for example.jsp (remember to import the Blobstore library):

<form enctype="multipart/form-data" method="post" action="<%= BlobstoreServiceFactory.getBlobstoreService().createUploadUrl("/blobstoreexample") %>">
<input type="file" name="file" size="30" />
<input type="submit" /></form>

You can add additional form elements to the form if needed; the servlet will be able to access them.

An Introduction To The Datastore In Java

Storing Data

Essentially, the datastore is a highly replicated, highly reliable place to store data. However, it is not a SQL database that you may have experience with. Instead, the datastore stores Entities, which are grouped into categories called kinds. Each entity has a unique ID or name – Google App Engine can set a unique ID, or you can set a name in your application code.

To create an entity with a mail kind, we use this line (App Engine allocates a unique ID for us automatically):

Entity entity = new Entity("mail");

Entities can have properties, which are name/valid pairs. To add a property, do this:

entity.setProperty("subject", subject); //Key, then value.

Here’s a complete code example to create an entity, set properties, and store it:

Entity entity = new Entity("mail");//Creates the entity with a kind of "mail", GAE automatically allocates an ID
entity.setUnindexedProperty("from", from);
entity.setProperty("subject", subject);
entity.setProperty("add_date", new Date());//Records the date this entity was added.
DatastoreServiceFactory.getDatastoreService().put(entity);

Note the use of setUnindexedProperty. This method informs App Engine that we won’t be searching on this property, so don’t build a search index for it. This is useful to reduce the cost of storing entities.

Datastore Query

Querying the datastore is simple as well.

Here’s an example piece of code which queries for all Entities with the kind of mail, and requiring that all properties returned have the property add_date greater than the value stored in date_var :

Query q = new Query("mail");
Query.Filter query_filter = new Query.FilterPredicate("add_date", Query.FilterOperator.GREATER_THAN_OR_EQUAL, date_var);
q.setFilter(query_filter);
PreparedQuery pq = DatastoreServiceFactory.getDatastoreService().prepare(q);
List<Entity> results = pq.asList(FetchOptions.Builder.withDefaults());

From here, we can iterate through the List, retrieve each Entity, and conduct operations on each Entity:

for (int i = 0; i < results.size(); i++) {
    Entity entity = results.get(i);
    //Do anything needed with the information from each entity.
}

Deleting

And finally, here is how to delete an entity (entity is the Entity object being deleted).

Key key = entity.getKey();
DatastoreServiceFactory.getAsyncDatastoreService().delete(key);

Deadline exceeded while waiting for HTTP response from URL

Occasionally applications – even the best behaved applications – will get the error “Deadline exceeded while waiting for HTTP response from URL.”

Generally, this means that the web service you’re trying to connect to is down or slow. If the service is down, then you can continuously retry your URL fetches by queuing them up within a task.

If the web service is slow, then you have an alternative: setting the read and connect timeouts to a longer timeout point. By default, App Engine expects that an URL fetch will take – at most – 5 seconds. That’s 5 seconds to connect to the web service (resolve DNS and so forth), send the request data, allow the web service to process the request, and finally retrieve any response sent back. For the vast majority of applications, that’s more than enough. The popular web APIs such as Twitter, Facebook, Google, etc all process and return requests in much less than 5 seconds.

However, a slow or malfunctioning web service may take longer than 5 seconds to respond to a query. If your app is downloading a large amount of data (more than a few MB) you may also go past this limit. To tell App Engine to wait for a longer period of time, use this code (url_connection represents a HttpURLConnection object):

url_connection.setReadTimeout(milliseconds_to_wait_for_read);
url_connection.setConnectTimeout(milliseconds_to_wait_for_connect);

Remember that the time to wait is denoted in milliseconds, so do the appropriate conversions (for example, if you wanted the connection to wait 30 seconds, you would put 30000 milliseconds).

Serving URL For Images Stored In Cloud Storage

Creating a serving URL for images stored in GCS is slightly different than for images stored in the Blobstore. Here’s a code sample:

Bucket represents the name of your Google Cloud Storage bucket, and object represents the file name of the image (including extension).

    String bucket = "bucket_name";
    String object = "file_name_including_extension";

    //Get serving url
    String gs_blob_key = "/gs/" + bucket + "/" + object;
    BlobKey blob_key = BlobstoreServiceFactory.getBlobstoreService().createGsBlobKey(gs_blob_key);
    ServingUrlOptions serving_options = ServingUrlOptions.Builder.withBlobKey(blob_key);
    String serving_url = ImagesServiceFactory.getImagesService().getServingUrl(serving_options);
    System.out.println("Serving URL: " + serving_url);
    resp.getWriter().println(serving_url);

Writing Files To Google Cloud Storage

Writing a file to Google Cloud Storage is easy in Java. All you need is the Google Cloud Storage library.

The following code writes a file into GCS. Bucket represents the name of the Cloud Storage bucket, object represents the filename, mime represents the MIME type, and data represents a byte array with the contents of the file you’re writing.

    String bucket = "your_bucket_name";
    String object = "file_name_here_including_extension";

    GcsFilename gcs_filename = new GcsFilename(bucket, object);
    //File Options
    GcsFileOptions.Builder options_builder = new GcsFileOptions.Builder();
    options_builder = options_builder.mimeType(mime);
    GcsFileOptions options = options_builder.build();       
    GcsOutputChannel output = GcsServiceFactory.createGcsService().createOrReplace(gcs_filename, options);
    output.write(ByteBuffer.wrap(data));
    output.close(); 

Remember to set bucket permissions in Google Cloud Storage so your app can access the files.

Configuring A .Properties File In App Engine

Configuration settings for an application should never be hardcoded; instead, they should be configured through a separate properties file.

A properties file looks like this:

#This is a comment
key=value

The # symbol denotes a comment, while keys are separated from values with an = sign.

You can read this file into a Java application by using the following code. It reads in a properties file named app.properties located within the /WEB-INF/ folder (putting your properties files in /WEB-INF/ also keeps them private):

InputStream app_properties_stream = this.getServletContext().getResourceAsStream("/WEB-INF/app.properties");
Properties app_properties = new Properties();
//This will throw a NullPointerException if the properties file cannot be found, 
//and an IOException if an error occurs while reading.
app_properties.load(app_properties_stream);

From here you can retrieve values by calling:

String value = app_properties.getProperty("key");

Storing To The Memcache In Golang

A short Golang example: how to store to memcache in Go. To use this, you’ll need to import appengine/memcache.

This code fragment generates a memcache.Item struct, to which you’ll need to supply a key and the value you want stored (a struct, array, map, etc).

memcache_item := &memcache.Item {
    Key: "key_to_this_object",
    Object: item_to_store,
}

//Store into Memcache
memcache.JSON.Set(c, memcache_item)

Accessing POP3 & IMAP From AppEngine

A quick note about accessing IMAP and POP3 servers from AppEngine: you need to include the JavaMail libraries and import everything from javax.mail and javax.mail.internet.

If you need to streamline the amount of JARs bundled with your application, you can also select individual protocol provider JARs.

Here’s example code to extract emails from a POP3 store:

    Properties properties = new Properties();
    properties.put("mail.host", host);
    properties.put("mail.store.protocol", "pop3s");
    properties.put("mail.pop3s.auth", "true");
    properties.put("mail.pop3s.port", port);
    Session session = Session.getDefaultInstance(properties);
    Store store = session.getStore();
    //With a POP3 store available, connect to the given account.
    store.connect(host, user, password);
    //Open up the inbox folder, and give ourselves read/write privilegese.
    Folder folder = store.getFolder("inbox");
    folder.open(Folder.READ_WRITE);
    //Collect messages.
    Message[] messages = folder.getMessages();
    System.out.println(messages.length);
    for (int i = 0; i < messages.length; i++) {
        //Extract message.
        MimeMessage message = (MimeMessage)messages[i];

From here, you can extract the from address and the subject line from the MimeMessage. Remember that the Sockets API requires that you have billing enabled on your application.

A Simple index.yaml File

The App Engine SDK developer server can automatically create datastore indexes for your application – just run the datastore querying code within the dev server. But sometimes that isn’t possible.

In that case, you can hand-write your index settings. Here is an example of a simple index.yaml file. It indexes all entities with a kind of Fact, on the property Approved and sorts on the property Add_date in descending order.

indexes:

- kind: Fact
  ancestor: no
  properties:
  - name: Approved
  - name: Add_date
    direction: desc

index.yaml goes in the root directory of your application.

Sending Mail With Golang

Previously I published a Java code example using the low level Mail API to send a message to the registered admins of an application. Here’s sample code for a Golang application to send mail to app admins. C stands for an appengine.Context reference.

If you want to send mail to an arbitrary user, and not an admin, you can uncomment the To line and change SendToAdmins() to Send().

application_id := appengine.AppID(c)
separation_point := strings.Index(application_id, ":")
if separation_point > -1 {
    application_id = application_id[separation_point:]
}

//Create the message struct
msg := &mail.Message{
    Sender:  "donotreply@" + application_id + ".appspotmail.com",
    //To:    []string{"To-User <[email protected]>"},
    Subject: subject,
    Body:    email_body,
}
c.Infof("Sending message: %v", msg)

//Send an email to admins
err := mail.SendToAdmins(c, msg)
if err != nil {
    c.Errorf("Unable to send email: %v", err)
}