Using Cloud Storage with Java

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

This page is part of a multipage tutorial. To start from the beginning and read the setup instructions, go to Java Bookshelf app.

Creating a Cloud Storage bucket

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

  1. In your terminal window, create a Cloud Storage bucket, where YOUR_BUCKET_NAME represents the name of your bucket:

    gsutil mb gs://YOUR_BUCKET_NAME
    
  2. To view uploaded images in the Bookshelf app, set the bucket's default access control list (ACL) to public-read:

    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 the following command to start a local web server. Replace [YOUR_PROJECT_ID] with your Google Cloud project ID:

    mvn -Plocal clean jetty:run-exploded -DprojectID=[YOUR-PROJECT-ID]
  2. In your web browser, go 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. Deploy the app.

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

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

When you update your app, you can deploy the updated version using the same command you used when you first deployed the app. 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. All of these app versions and VM instances are billable resources.

You can reduce costs by deleting the non-default versions of your app. For complete information about cleaning up billable resources, see the Cleaning up section in the final step of this tutorial.

App structure

As illustrated in the following diagram, the app uses Cloud Storage to store binary data, such as pictures in this case. The app continues to use Datastore for book information.

Binary data sample structure

Understanding the code

This section walks you through the app's code and explains how it works.

Handle user uploads

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

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

Upload blobs to Cloud Storage

Read the file from a POST request by 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.
 */
// Note: this sample assumes small files are uploaded. For large files or streams use:
// Storage.writer(BlobInfo blobInfo, Storage.BlobWriteOption... options)
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
  // Read InputStream into a ByteArrayOutputStream.
  InputStream is = filePart.getInputStream();
  ByteArrayOutputStream os = new ByteArrayOutputStream();
  byte[] readBuf = new byte[4096];
  while (is.available() > 0) {
    int bytesRead = is.read(readBuf);
    os.write(readBuf, 0, bytesRead);
  }

  // Convert ByteArrayOutputStream into byte[]
  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(),
          os.toByteArray());
  // return the public download link
  return blobInfo.getMediaLink();
}
Was this page helpful? Let us know how we did:

Send feedback about...