From 293219abbbbe1828fed519c98e09296a1e3f2caf Mon Sep 17 00:00:00 2001 From: Les Vogel Date: Fri, 29 Apr 2016 15:12:46 -0700 Subject: [PATCH] Submit Images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A few modules weren’t added to pom as well, that should be fixed. --- appengine/images/README.md | 36 ++++++ appengine/images/pom.xml | 73 +++++++++++ .../appengine/images/ImagesServlet.java | 118 ++++++++++++++++++ .../src/main/webapp/WEB-INF/appengine-web.xml | 20 +++ .../images/src/main/webapp/WEB-INF/image.jpg | Bin 0 -> 7933 bytes .../images/src/main/webapp/WEB-INF/web.xml | 14 +++ pom.xml | 4 + 7 files changed, 265 insertions(+) create mode 100644 appengine/images/README.md create mode 100644 appengine/images/pom.xml create mode 100644 appengine/images/src/main/java/com/example/appengine/images/ImagesServlet.java create mode 100644 appengine/images/src/main/webapp/WEB-INF/appengine-web.xml create mode 100644 appengine/images/src/main/webapp/WEB-INF/image.jpg create mode 100644 appengine/images/src/main/webapp/WEB-INF/web.xml diff --git a/appengine/images/README.md b/appengine/images/README.md new file mode 100644 index 00000000000..88edaa825f2 --- /dev/null +++ b/appengine/images/README.md @@ -0,0 +1,36 @@ +# Google App Engine Standard Environment Images Sample + +This sample demonstrates how to use the Images Java API. + +See the [Google App Engine standard environment documentation][ae-docs] for more +detailed instructions. + +[ae-docs]: https://cloud.google.com/appengine/docs/java/ + +## Modify the app + +Using the [Google Cloud SDK](https://cloud.google.com/sdk/) create a bucket + + $ gsutil mb YOUR-PROJECT-ID.appspot.com + +* Edit `src/main/java/com/example/appengine/images/ImageServlet.java` and set your `bucket` name. + +## Running locally + + This example uses the + [App Engine maven plugin](https://cloud.google.com/appengine/docs/java/tools/maven). + To run this sample locally: + + $ mvn appengine:devserver + + To see the results of the sample application, open + [localhost:8080](http://localhost:8080) in a web browser. + + +## Deploying + + In the following command, replace YOUR-PROJECT-ID with your + [Google Cloud Project ID](https://developers.google.com/console/help/new/#projectnumber) + and SOME-VERSION with a valid version number. + + $ mvn appengine:update -Dappengine.appId=YOUR-PROJECT-ID -Dappengine.version=SOME-VERSION diff --git a/appengine/images/pom.xml b/appengine/images/pom.xml new file mode 100644 index 00000000000..afd735da9aa --- /dev/null +++ b/appengine/images/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + war + 1.0-SNAPSHOT + com.example.appengine + appengine-images + + + 1.9.24 + + + + com.google.cloud + doc-samples + 1.0.0 + ../.. + + + + + com.google.appengine + appengine-api-1.0-sdk + ${appengine.sdk.version} + + + com.google.appengine.tools + appengine-gcs-client + RELEASE + + + javax.servlet + servlet-api + jar + provided + + + + + ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + + org.apache.maven.plugins + 3.3 + maven-compiler-plugin + + 1.7 + 1.7 + + + + + com.google.appengine + appengine-maven-plugin + ${appengine.sdk.version} + + + + diff --git a/appengine/images/src/main/java/com/example/appengine/images/ImagesServlet.java b/appengine/images/src/main/java/com/example/appengine/images/ImagesServlet.java new file mode 100644 index 00000000000..a864cd62c64 --- /dev/null +++ b/appengine/images/src/main/java/com/example/appengine/images/ImagesServlet.java @@ -0,0 +1,118 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.appengine.images; +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.ImagesService; +import com.google.appengine.api.images.ImagesServiceFactory; +import com.google.appengine.api.images.Transform; +import com.google.appengine.tools.cloudstorage.GcsFileOptions; +import com.google.appengine.tools.cloudstorage.GcsFilename; +import com.google.appengine.tools.cloudstorage.GcsService; +import com.google.appengine.tools.cloudstorage.GcsServiceFactory; +import com.google.appengine.tools.cloudstorage.RetryParams; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +// [START example] +@SuppressWarnings("serial") +public class ImagesServlet extends HttpServlet { + final String bucket = "YOUR-BUCKETNAME-HERE"; + + // [START gcs] + private final GcsService gcsService = GcsServiceFactory.createGcsService(new RetryParams.Builder() + .initialRetryDelayMillis(10) + .retryMaxAttempts(10) + .totalRetryPeriodMillis(15000) + .build()); + // [END gcs] + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + + //[START original_image] + // Read the image.jpg resource into a ByteBuffer. + FileInputStream fileInputStream = new FileInputStream(new File("WEB-INF/image.jpg")); + FileChannel fileChannel = fileInputStream.getChannel(); + ByteBuffer byteBuffer = ByteBuffer.allocate((int)fileChannel.size()); + fileChannel.read(byteBuffer); + + byte[] imageBytes = byteBuffer.array(); + + // Write the original image to Cloud Storage + gcsService.createOrReplace( + new GcsFilename(bucket, "image.jpeg"), + new GcsFileOptions.Builder().mimeType("image/jpeg").build(), + ByteBuffer.wrap(imageBytes)); + //[END original_image] + + //[START resize] + // Get an instance of the imagesService we can use to transform images. + ImagesService imagesService = ImagesServiceFactory.getImagesService(); + + // Make an image directly from a byte array, and transform it. + Image image = ImagesServiceFactory.makeImage(imageBytes); + Transform resize = ImagesServiceFactory.makeResize(100, 50); + Image resizedImage = imagesService.applyTransform(resize, image); + + // Write the transformed image back to a Cloud Storage object. + gcsService.createOrReplace( + new GcsFilename(bucket, "resizedImage.jpeg"), + new GcsFileOptions.Builder().mimeType("image/jpeg").build(), + ByteBuffer.wrap(resizedImage.getImageData())); + //[END resize] + + //[START rotate] + // Make an image from a Cloud Storage object, and transform it. + BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService(); + BlobKey blobKey = blobstoreService.createGsBlobKey("gs://" + bucket + "/image.jpeg"); + Image blobImage = ImagesServiceFactory.makeImageFromBlob(blobKey); + Transform rotate = ImagesServiceFactory.makeRotate(90); + Image rotatedImage = imagesService.applyTransform(rotate, blobImage); + + // Write the transformed image back to a Cloud Storage object. + gcsService.createOrReplace( + new GcsFilename(bucket, "rotatedImage.jpeg"), + new GcsFileOptions.Builder().mimeType("image/jpeg").build(), + ByteBuffer.wrap(rotatedImage.getImageData())); + //[END rotate] + + // Output some simple HTML to display the images we wrote to Cloud Storage + // in the browser. + PrintWriter out = resp.getWriter(); + out.println("\n"); + out.println("AppEngine logo"); + out.println("AppEngine logo resized"); + out.println("AppEngine logo rotated"); + out.println("\n"); + } +} +// [END example] diff --git a/appengine/images/src/main/webapp/WEB-INF/appengine-web.xml b/appengine/images/src/main/webapp/WEB-INF/appengine-web.xml new file mode 100644 index 00000000000..07bb4d7de99 --- /dev/null +++ b/appengine/images/src/main/webapp/WEB-INF/appengine-web.xml @@ -0,0 +1,20 @@ + + + + + + YOUR-PROJECT-ID + YOUR-VERSION + true + diff --git a/appengine/images/src/main/webapp/WEB-INF/image.jpg b/appengine/images/src/main/webapp/WEB-INF/image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3a60da2619de76a5461d91e40103e835f45c8eeb GIT binary patch literal 7933 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 Xju$)sSCzvlr`!tppN5~=<>dbWU!w^= literal 0 HcmV?d00001 diff --git a/appengine/images/src/main/webapp/WEB-INF/web.xml b/appengine/images/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..8ccba622877 --- /dev/null +++ b/appengine/images/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,14 @@ + + + + images + com.example.appengine.images.ImagesServlet + + + images + / + + diff --git a/pom.xml b/pom.xml index bd885080d87..96369b9ed33 100644 --- a/pom.xml +++ b/pom.xml @@ -46,12 +46,14 @@ appengine/analytics appengine/appidentity appengine/channel + appengine/cloudsql appengine/datastore appengine/datastore/indexes appengine/datastore/indexes-exploding appengine/datastore/indexes-perfect appengine/guestbook-objectify appengine/helloworld + appengine/images appengine/logs appengine/mailgun appengine/mailjet @@ -71,11 +73,13 @@ appengine/xmpp bigquery compute/cmdline + compute/sendgrid datastore logging managed_vms/analytics managed_vms/async-rest managed_vms/cloudstorage + managed_vms/cron managed_vms/datastore managed_vms/disk managed_vms/extending-runtime