Using Cloud Storage with Java

This part of the Bookshelf tutorial for Java shows how to store images in Google Cloud Storage.

This page is part of a multi-page tutorial. To start from the beginning and see instructions for setting up, go to Java Bookshelf App.

Creating a Cloud Storage bucket

The following instructions show how to create a Cloud Storage bucket. Buckets are the basic containers that hold your data in Cloud Storage.

To create a bucket:

  1. Invoke the following command in a terminal window:

    gsutil mb gs://[YOUR-BUCKET-NAME]

  2. Set the bucket's default ACL to public-read, which enables users to see their uploaded images:

    gsutil defacl set public-read gs://[YOUR-BUCKET-NAME]

Configuring settings

Running the app on your local machine

To run the app locally:

  1. In the getting-started-java/bookshelf/3-binary-data directory, enter this command to start a local web server:

    mvn -Plocal clean jetty:run-exploded -DprojectID=[YOUR-PROJECT-ID]
  2. In your web browser, navigate to http://localhost:8080

Now you can browse the app's web pages, add books with cover images, edit books, and delete books.

Deploying the app to the App Engine flexible environment

  1. Enter this command to deploy the app:

    mvn appengine:deploy -DprojectID=YOUR-PROJECT-ID
    
  2. In your web browser, enter this address. Replace [YOUR_PROJECT_ID] with your project ID:

    https://[YOUR_PROJECT_ID].appspot-preview.com
    

If you update your app, you can deploy the updated version by entering the same command you used to deploy the app the first time. The new deployment creates a new version of your app and promotes it to the default version. The older versions of your app remain, as do their associated VM instances. Be aware that all of these app versions and VM instances are billable resources.

You can reduce costs by deleting the non-default versions of your app.

To delete an app version:

  1. In the GCP Console, go to the App Engine Versions page.

    Go to the Versions page

  2. Click the checkbox next to the non-default app version you want to delete.
  3. Click the Delete button at the top of the page to delete the app version.

For complete information about cleaning up billable resources, see the Cleaning up section in the final step of this tutorial.

Application structure

Binary data sample structure

The application uses Cloud Storage to store binary data, pictures in this case. The app continues to use Cloud Datastore for book information.

Understanding the code

This section walks you through the application code and explains how it works.

Handling user uploads

It is simple to use the Google Cloud Storage API for Java. In most cases, a single line is all you need to authenticate locally.

static {
  storage = StorageOptions.getDefaultInstance().getService();
}

Uploading blobs to Cloud Storage

Read the file from a POST request using getPart. Verify the file matches your requirements before uploading it.

/**
 * Extracts the file payload from an HttpServletRequest, checks that the file extension
 * is supported and uploads the file to Google Cloud Storage.
 */
public String getImageUrl(HttpServletRequest req, HttpServletResponse resp,
                          final String bucket) throws IOException, ServletException {
  Part filePart = req.getPart("file");
  final String fileName = filePart.getSubmittedFileName();
  String imageUrl = req.getParameter("imageUrl");
  // Check extension of file
  if (fileName != null && !fileName.isEmpty() && fileName.contains(".")) {
    final String extension = fileName.substring(fileName.lastIndexOf('.') + 1);
    String[] allowedExt = { "jpg", "jpeg", "png", "gif" };
    for (String s : allowedExt) {
      if (extension.equals(s)) {
        return this.uploadFile(filePart, bucket);
      }
    }
    throw new ServletException("file must be an image");
  }
  return imageUrl;
}

Then, make the filename unique by appending a timestamp to it. Using storage.create, pass a BlobInfo created with bucketName and fileName, and set the access control list so that all users can read and pass the file's InputStream. The result is the public URL for the object.

/**
 * Uploads a file to Google Cloud Storage to the bucket specified in the BUCKET_NAME
 * environment variable, appending a timestamp to end of the uploaded filename.
 */
@SuppressWarnings("deprecation")
public String uploadFile(Part filePart, final String bucketName) throws IOException {
  DateTimeFormatter dtf = DateTimeFormat.forPattern("-YYYY-MM-dd-HHmmssSSS");
  DateTime dt = DateTime.now(DateTimeZone.UTC);
  String dtString = dt.toString(dtf);
  final String fileName = filePart.getSubmittedFileName() + dtString;

  // the inputstream is closed by default, so we don't need to close it here
  BlobInfo blobInfo =
      storage.create(
          BlobInfo
              .newBuilder(bucketName, fileName)
              // Modify access list to allow all users with link to read file
              .setAcl(new ArrayList<>(Arrays.asList(Acl.of(User.ofAllUsers(), Role.READER))))
              .build(),
          filePart.getInputStream());
  // return the public download link
  return blobInfo.getMediaLink();
}

Send feedback about...