Heading | Description |
---|---|
Description | Discusses how to develop a dynamic web application that analyzes nature images located in an Amazon Simple Storage Service (Amazon S3) bucket by using the AWS SDK for Java V2. |
Audience | Developer (beginner / intermediate) |
Required Skills | Java, Maven |
You can create a dynamic web application that analyzes nature images located in an Amazon S3 bucket by using the Amazon Rekognition service. The application analyzes many images and generates a report that breaks down each image into a series of labels. For example, the following image shows a lake.
After the application analyzes all images in the Amazon S3 bucket, it uses the Amazon Simple Email Service (Amazon SES) to send a dynamically created report to a given email recipient. The report is Microsoft Excel data that contains labels for each image located in the Amazon S3 bucket, as shown in this illustration.
In this tutorial, you create a Spring Boot application named AWS Photo Analyzer. The Spring Boot APIs are used to build a model, different views, and a controller. For more information, see Spring Boot.
This application uses the following AWS services:
- Amazon Rekognition
- Amazon S3
- Amazon SES
- Prerequisites
- Understand the AWS Photo Analyzer application
- Create an IntelliJ project named SpringPhotoAnalyzer
- Add the POM dependencies to your project
- Create the Java classes
- Create the HTML files
- Create the script files
- Run the application
To complete the tutorial, you need the following:
- An AWS account
- A Java IDE (this tutorial uses the IntelliJ IDE)
- Java JDK 17
- Maven 3.6 or later
- The AWS services included in this document are included in the AWS Free Tier.
- This code has not been tested in all AWS Regions. Some AWS services are available only in specific regions. For more information, see AWS Regional Services.
- Running this code might result in charges to your AWS account.
- Be sure to terminate all of the resources you create while going through this tutorial to ensure that you’re not charged.
Create an Amazon S3 bucket named photos[somevalue]. Be sure to use this bucket name in your Amazon S3 Java code. For information, see Creating a bucket.
In addition, make sure that you have properly setup your development environment. For information, see Setting up the AWS SDK for Java 2.x.
The AWS Photo Analyzer application supports uploading images to an Amazon S3 bucket. After the images are uploaded, you can view the images that are analyzed.
To generate a report, enter an email address and choose Analyze Photos.
You can also download a given image from the Amazon S3 bucket by using this application. Simply specify the image name and choose the Download Photo button. The image is downloaded to your browser, as shown in this illustration.
- In the IntelliJ IDE, choose File, New, Project.
- In the New Project dialog box, choose Maven, and then choose Next.
- For GroupId, enter aws-spring.
- For ArtifactId, enter SpringPhotoAnalyzer.
- Choose Next.
- Choose Finish.
At this point, you have a new project named SpringPhotoAnalyzer.
Note: Ensure that you are using Java 17 (as shown in the following pom.xml file).
Make sure that your project's pom.xml file looks like the POM file in this Github repository.
Create a Java package in the main/java folder named com.example.photo.
The Java files go into this package.
Create these Java classes:
- AnalyzePhotos - Uses the Amazon Rekognition API to analyze the images.
- BucketItem - Used as a model that stores Amazon S3 bucket information.
- PhotoApplication - Used as the base class for the Spring Boot application.
- PhotoController - Used as the Spring Boot controller that handles HTTP requests.
- SendMessages - Uses the Amazon SES API to send an email message with an attachment.
- S3Service - Uses the Amazon S3 API to perform operations.
- WorkItem - Used as a model that stores Amazon Rekognition data.
- WriteExcel – Uses the JXL API (this is not an AWS API) to dynamically generate a report.
The following Java code represents the AnalyzePhotos class. This class uses the Amazon Rekognition API to analyze the images.
package com.example.photo;
import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.rekognition.RekognitionClient;
import software.amazon.awssdk.services.rekognition.model.Image;
import software.amazon.awssdk.services.rekognition.model.DetectLabelsRequest;
import software.amazon.awssdk.services.rekognition.model.DetectLabelsResponse;
import software.amazon.awssdk.services.rekognition.model.Label;
import software.amazon.awssdk.services.rekognition.model.RekognitionException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Component;
@Component
public class AnalyzePhotos {
public ArrayList<WorkItem> DetectLabels(byte[] bytes, String key) {
try {
RekognitionClient rekClient = RekognitionClient.builder()
.credentialsProvider(EnvironmentVariableCredentialsProvider.create())
.region(Region.US_EAST_2)
.build();
SdkBytes sourceBytes = SdkBytes.fromByteArray(bytes);
Image souImage = Image.builder()
.bytes(sourceBytes)
.build();
DetectLabelsRequest detectLabelsRequest = DetectLabelsRequest.builder()
.image(souImage)
.maxLabels(10)
.build();
DetectLabelsResponse labelsResponse = rekClient.detectLabels(detectLabelsRequest);
List<Label> labels = labelsResponse.labels();
System.out.println("Detected labels for the given photo");
ArrayList<WorkItem> list = new ArrayList<>();
WorkItem item ;
for (Label label: labels) {
item = new WorkItem();
item.setKey(key); // identifies the photo
item.setConfidence(label.confidence().toString());
item.setName(label.name());
list.add(item);
}
return list;
} catch (RekognitionException e) {
System.out.println(e.getMessage());
System.exit(1);
}
return null ;
}
}
Note: In this example, an EnvironmentVariableCredentialsProvider is used for the credentials.
The following Java code represents the BucketItem class that stores S3 object data.
package com.example.photo;
public class BucketItem {
private String key;
private String owner;
private String date ;
private String size ;
public void setSize(String size) {
this.size = size ;
}
public String getSize() {
return this.size ;
}
public void setDate(String date) {
this.date = date ;
}
public String getDate() {
return this.date ;
}
public void setOwner(String owner) {
this.owner = owner ;
}
public String getOwner() {
return this.owner ;
}
public void setKey(String key) {
this.key = key ;
}
public String getKey() {
return this.key ;
}
}
The following Java code represents the PhotoApplication class.
package com.example.photo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class PhotoApplication {
public static void main(String[] args) {
SpringApplication.run(PhotoApplication.class, args);
}
}
The following Java code represents the PhotoController class that handles HTTP requests. For example, when a new image is posted (uploaded to an S3 bucket), the singleFileUpload method handles the request.
Note: Be sure that you change the bucketName variable to your Amazon S3 bucket name.
package com.example.photo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.view.RedirectView;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
@Controller
public class PhotoController {
// Change to your Bucket Name
private final String bucketName = "<Enter your Amazon S3 bucket name>";
private final S3Service s3Service;
private final AnalyzePhotos photos;
private final WriteExcel excel;
private final SendMessages sendMessage;
@Autowired
PhotoController(
S3Service s3Service,
AnalyzePhotos photos,
WriteExcel excel,
SendMessages sendMessage
) {
this.s3Service = s3Service;
this.photos = photos;
this.excel = excel;
this.sendMessage = sendMessage;
}
@GetMapping("/")
public String root() {
return "index";
}
@GetMapping("/process")
public String process() {
return "process";
}
@GetMapping("/photo")
public String photo() {
return "upload";
}
@RequestMapping(value = "/getimages", method = RequestMethod.GET)
@ResponseBody
String getImages(HttpServletRequest request, HttpServletResponse response) {
return s3Service.ListAllObjects(bucketName);
}
// Generates a report that analyzes photos in a given bucket.
@RequestMapping(value = "/report", method = RequestMethod.POST)
@ResponseBody
String report(HttpServletRequest request, HttpServletResponse response) {
// Get a list of key names in the given bucket.
String email = request.getParameter("email");
ArrayList<String> myKeys = s3Service.ListBucketObjects(bucketName);
ArrayList<List> myList = new ArrayList<>();
for (String myKey : myKeys) {
byte[] keyData = s3Service.getObjectBytes(bucketName, myKey);
ArrayList<WorkItem> item = photos.DetectLabels(keyData, myKey);
myList.add(item);
}
// Now we have a list of WorkItems describing the photos in the S3 bucket.
InputStream excelData = excel.exportExcel(myList);
try {
// Email the report.
sendMessage.sendReport(excelData, email);
} catch (Exception e) {
e.printStackTrace();
}
return "The photos have been analyzed and the report is sent";
}
// Upload a video to analyze.
@RequestMapping(value = "/upload", method = RequestMethod.POST)
@ResponseBody
public ModelAndView singleFileUpload(@RequestParam("file") MultipartFile file) {
try {
// Put the file into the bucket.
byte[] bytes = file.getBytes();
String name = file.getOriginalFilename() ;
s3Service.putObject(bytes, bucketName, name);
} catch (IOException e) {
e.printStackTrace();
}
return new ModelAndView(new RedirectView("photo"));
}
// This controller method downloads the given image from the Amazon S3 bucket.
@RequestMapping(value = "/downloadphoto", method = RequestMethod.GET)
void buildDynamicReportDownload(HttpServletRequest request, HttpServletResponse response) {
try {
String photoKey = request.getParameter("photoKey");
byte[] photoBytes = s3Service.getObjectBytes(bucketName, photoKey) ;
InputStream is = new ByteArrayInputStream(photoBytes);
// Define the required information here.
response.setContentType("image/png");
response.setHeader("Content-disposition", "attachment; filename="+photoKey);
org.apache.commons.io.IOUtils.copy(is, response.getOutputStream());
response.flushBuffer();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Note - Be sure to replace the bucket name in this code example with your bucket name.
The following class uses the Amazon S3 Java API to perform Amazon S3 operations. For example, the getObjectBytes method returns a byte array that represents the image.
package com.example.photo;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
import software.amazon.awssdk.core.ResponseBytes;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringWriter;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@Component
public class S3Service {
S3Client s3 ;
// Create the S3Client object.
private S3Client getClient() {
return S3Client.builder()
.credentialsProvider(EnvironmentVariableCredentialsProvider.create())
.region(Region.US_WEST_2)
.build();
}
// Get the byte[] from this Amazon S3 object.
public byte[] getObjectBytes (String bucketName, String keyName) {
s3 = getClient();
try {
GetObjectRequest objectRequest = GetObjectRequest
.builder()
.key(keyName)
.bucket(bucketName)
.build();
ResponseBytes<GetObjectResponse> objectBytes = s3.getObjectAsBytes(objectRequest);
return objectBytes.asByteArray();
} catch (S3Exception e) {
System.err.println(e.awsErrorDetails().errorMessage());
System.exit(1);
}
return null;
}
// Returns the names of all images and data within an XML document.
public String ListAllObjects(String bucketName) {
s3 = getClient();
long sizeLg;
Instant DateIn;
BucketItem myItem ;
List<BucketItem> bucketItems = new ArrayList<>();
try {
ListObjectsRequest listObjects = ListObjectsRequest
.builder()
.bucket(bucketName)
.build();
ListObjectsResponse res = s3.listObjects(listObjects);
List<S3Object> objects = res.contents();
for (S3Object myValue : objects) {
myItem = new BucketItem();
myItem.setKey(myValue.key());
myItem.setOwner(myValue.owner().displayName());
sizeLg = myValue.size() / 1024;
myItem.setSize(String.valueOf(sizeLg));
DateIn = myValue.lastModified();
myItem.setDate(String.valueOf(DateIn));
// Push the items to the list.
bucketItems.add(myItem);
}
return convertToString(toXml(bucketItems));
} catch (S3Exception e) {
System.err.println(e.awsErrorDetails().errorMessage());
System.exit(1);
}
return null ;
}
// Returns the names of all images in the given bucket.
public ArrayList<String> ListBucketObjects(String bucketName) {
s3 = getClient();
String keyName ;
ArrayList<String> keys = new ArrayList<String>();
try {
ListObjectsRequest listObjects = ListObjectsRequest
.builder()
.bucket(bucketName)
.build();
ListObjectsResponse res = s3.listObjects(listObjects);
List<S3Object> objects = res.contents();
for (S3Object myValue : objects) {
keyName = myValue.key();
keys.add(keyName);
}
return keys;
} catch (S3Exception e) {
System.err.println(e.awsErrorDetails().errorMessage());
System.exit(1);
}
return null ;
}
// Places an image into a S3 bucket.
public void putObject(byte[] data, String bucketName, String objectKey) {
s3 = getClient();
try {
s3.putObject(PutObjectRequest.builder()
.bucket(bucketName)
.key(objectKey)
.build(),
RequestBody.fromBytes(data));
} catch (S3Exception e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
// Convert items into XML to pass back to the view.
private Document toXml(List<BucketItem> itemList) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.newDocument();
// Start building the XML.
Element root = doc.createElement( "Items" );
doc.appendChild( root );
// Iterate through the collection.
for (BucketItem myItem : itemList) {
// Get the WorkItem object from the collection.
Element item = doc.createElement("Item");
root.appendChild(item);
// Set Key.
Element id = doc.createElement("Key");
id.appendChild(doc.createTextNode(myItem.getKey()));
item.appendChild(id);
// Set Owner.
Element name = doc.createElement("Owner");
name.appendChild(doc.createTextNode(myItem.getOwner()));
item.appendChild(name);
// Set Date.
Element date = doc.createElement("Date");
date.appendChild(doc.createTextNode(myItem.getDate()));
item.appendChild(date);
// Set Size.
Element desc = doc.createElement("Size");
desc.appendChild(doc.createTextNode(myItem.getSize()));
item.appendChild(desc);
}
return doc;
} catch(ParserConfigurationException e) {
e.printStackTrace();
}
return null;
}
private String convertToString(Document xml) {
try {
TransformerFactory transformerFactory = getSecureTransformerFactory();
Transformer transformer = transformerFactory.newTransformer();
StreamResult result = new StreamResult(new StringWriter());
DOMSource source = new DOMSource(xml);
transformer.transform(source, result);
return result.getWriter().toString();
} catch(TransformerException ex) {
ex.printStackTrace();
}
return null;
}
private static TransformerFactory getSecureTransformerFactory() {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
try {
transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
} catch (TransformerConfigurationException e) {
e.printStackTrace();
}
return transformerFactory;
}
}
The following Java code represents the SendMessage class. This class uses the Amazon SES Java API to send an email message with an attachment that represents the report.
package com.example.photo;
import org.apache.commons.io.IOUtils;
import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ses.SesClient;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeBodyPart;
import javax.mail.util.ByteArrayDataSource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.Properties;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.services.ses.model.SendRawEmailRequest;
import software.amazon.awssdk.services.ses.model.RawMessage;
import software.amazon.awssdk.services.ses.model.SesException;
import org.springframework.stereotype.Component;
@Component
public class SendMessages {
public void sendReport(InputStream is, String emailAddress ) throws IOException {
byte[] fileContent = IOUtils.toByteArray(is);
try {
send(fileContent,emailAddress);
} catch (MessagingException e) {
e.getStackTrace();
}
}
public void send(byte[] attachment, String emailAddress) throws MessagingException, IOException {
MimeMessage message = null;
Session session = Session.getDefaultInstance(new Properties());
message = new MimeMessage(session);
String subject = "Analyzed photos report";
message.setSubject(subject, "UTF-8");
String sender = "scmacdon@amazon.com";
message.setFrom(new InternetAddress(sender));
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(emailAddress));
// Create a multipart/alternative child container.
MimeMultipart msgBody = new MimeMultipart("alternative");
// Create a wrapper for the HTML and text parts.
MimeBodyPart wrap = new MimeBodyPart();
// Define the text part.
MimeBodyPart textPart = new MimeBodyPart();
// The email body for recipients with non-HTML email clients.
String bodyText = "Hello,\r\n" + "Please see the attached file for the analyzed photos report.";
textPart.setContent(bodyText, "text/plain; charset=UTF-8");
// Define the HTML part.
MimeBodyPart htmlPart = new MimeBodyPart();
// The HTML body of the email.
String bodyHTML = "<html>" + "<head></head>" + "<body>" + "<h1>Hello!</h1>"
+ "<p>Please see the attached file for the report that analyzed photos in the S3 bucket.</p>" + "</body>" + "</html>";
htmlPart.setContent(bodyHTML, "text/html; charset=UTF-8");
// Add the text and HTML parts to the child container.
msgBody.addBodyPart(textPart);
msgBody.addBodyPart(htmlPart);
// Add the child container to the wrapper object.
wrap.setContent(msgBody);
// Create a multipart/mixed parent container.
MimeMultipart msg = new MimeMultipart("mixed");
// Add the parent container to the message.
message.setContent(msg);
// Add the multipart/alternative part to the message.
msg.addBodyPart(wrap);
// Define the attachment
MimeBodyPart att = new MimeBodyPart();
DataSource fds = new ByteArrayDataSource(attachment, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
att.setDataHandler(new DataHandler(fds));
String reportName = "PhotoReport.xls";
att.setFileName(reportName);
// Add the attachment to the message.
msg.addBodyPart(att);
// Try to send the email.
try {
System.out.println("Attempting to send an email through Amazon SES " + "using the AWS SDK for Java...");
SesClient client = SesClient.builder()
.credentialsProvider(EnvironmentVariableCredentialsProvider.create())
.region(Region.US_WEST_2)
.build();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
message.writeTo(outputStream);
ByteBuffer buf = ByteBuffer.wrap(outputStream.toByteArray());
byte[] arr = new byte[buf.remaining()];
buf.get(arr);
SdkBytes data = SdkBytes.fromByteArray(arr);
RawMessage rawMessage = RawMessage.builder()
.data(data)
.build();
SendRawEmailRequest rawEmailRequest = SendRawEmailRequest.builder()
.rawMessage(rawMessage)
.build();
client.sendRawEmail(rawEmailRequest);
} catch (SesException e) {
System.err.println(e.awsErrorDetails().errorMessage());
System.exit(1);
}
System.out.println("Email sent with attachment");
}
}
Note: Make sure that you specify your validated email address. For information, see Creating and verifying identities in Amazon SES.
The following Java code represents the WorkItem class.
package com.example.photo;
public class WorkItem {
private String key;
private String name;
private String confidence ;
public void setKey (String key) {
this.key = key;
}
public String getKey() {
return this.key;
}
public void setName (String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setConfidence (String confidence) {
this.confidence = confidence;
}
public String getConfidence() {
return this.confidence;
}
}
The following Java code represents the WriteExcel class.
package com.example.photo;
import jxl.CellView;
import jxl.Workbook;
import jxl.WorkbookSettings;
import jxl.format.UnderlineStyle;
import jxl.write.Label;
import jxl.write.WritableCellFormat;
import jxl.write.WritableFont;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Locale;
@Component
public class WriteExcel {
private WritableCellFormat timesBoldUnderline;
private WritableCellFormat times;
// Returns an InputStream that represents the Excel Report.
public InputStream exportExcel(List<List<WorkItem>> list) {
try {
return write(list);
} catch (WriteException | IOException e) {
e.printStackTrace();
}
return null;
}
// Generates the report and returns an inputstream.
public InputStream write(List<List<WorkItem>> list) throws IOException, WriteException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
WorkbookSettings wbSettings = new WorkbookSettings();
wbSettings.setLocale(new Locale("en", "EN"));
WritableWorkbook workbook = Workbook.createWorkbook(os, wbSettings);
int size = list.size();
for (int i = 0; i < size; i++) {
// Get the WorkItem from each list.
List<WorkItem> innerList = list.get(i);
workbook.createSheet("Sheet " + (i + 1), i);
WritableSheet excelSheet = workbook.getSheet(i);
createLabel(excelSheet);
createContent(excelSheet, innerList);
}
// Close the workbook.
workbook.write();
workbook.close();
// Get an InputStream that represents the Report.
byte[] myBytes = os.toByteArray();
return new ByteArrayInputStream(myBytes);
}
// Create Headings in the Excel spreadsheet.
private void createLabel(WritableSheet sheet) throws WriteException {
// Create a times font.
WritableFont times10pt = new WritableFont(WritableFont.TIMES, 10);
// Define the cell format.
times = new WritableCellFormat(times10pt);
// Let's automatically wrap the cells.
times.setWrap(true);
// Create a bold font with underlines.
WritableFont times10ptBoldUnderline = new WritableFont(WritableFont.TIMES, 10, WritableFont.BOLD, false,
UnderlineStyle.SINGLE);
timesBoldUnderline = new WritableCellFormat(times10ptBoldUnderline);
// Let's automatically wrap the cells.
timesBoldUnderline.setWrap(true);
CellView cv = new CellView();
cv.setFormat(times);
cv.setFormat(timesBoldUnderline);
cv.setAutosize(true);
// Write a few headers.
addCaption(sheet, 0, 0, "Photo");
addCaption(sheet, 1, 0, "Label");
addCaption(sheet, 2, 0, "Confidence");
}
// Write the WorkItem Data to the Excel Report.
private void createContent(WritableSheet sheet, List<WorkItem> list) throws WriteException {
int size = list.size();
for (int i = 0; i < size; i++) {
WorkItem wi = list.get(i);
String key = wi.getKey();
String label = wi.getName();
String confidence = wi.getConfidence();
// First column.
addLabel(sheet, 0, i + 1, key);
// Second column.
addLabel(sheet, 1, i + 1, label);
// Third column.
addLabel(sheet, 2, i + 1, confidence);
}
}
private void addCaption(WritableSheet sheet, int column, int row, String s) throws WriteException {
Label label = new Label(column, row, s, timesBoldUnderline);
int cc = countString(s);
sheet.setColumnView(column, cc);
sheet.addCell(label);
}
private void addLabel(WritableSheet sheet, int column, int row, String s) throws WriteException {
Label label = new Label(column, row, s, times);
int cc = countString(s);
if (cc > 200) {
sheet.setColumnView(column, 150);
} else {
sheet.setColumnView(column, cc + 6);
}
sheet.addCell(label);
}
private int countString(String ss) {
int count = 0;
for (int i = 0; i < ss.length(); i++) {
if (ss.charAt(i) != ' ') {
count++;
}
}
return count;
}
}
At this point, you have created all of the Java files required for the AWS Photo Analyzer application. Now you create the HTML files that are required for the application's graphical user interface (GUI). Under the resource folder, create a template folder, and then create the following HTML files:
- index.html
- process.html
- upload.html
- layout.html
The index.html file is the application's home view. The process.html file represents the view for creating a report. The upload.html file represents the view for uploading image files to an S3 bucket. The layout.html file represents the menu that's visible in all views.
The following HTML represents the index.html file.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" th:href="|https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css|"/>
<script th:src="|https://code.jquery.com/jquery-1.12.4.min.js|"></script>
<script th:src="|https://code.jquery.com/ui/1.11.4/jquery-ui.min.js|"></script>
<link rel="stylesheet" href="../public/css/styles.css" th:href="@{/css/styles.css}" />
<link rel="icon" href="../public/images/favicon.ico" th:href="@{/images/favicon.ico}" />
<title>AWS Photo Analyzer</title>
</head>
<body>
<header th:replace="layout :: site-header"/>
<div class="container">
<h2>AWS Photo Analyzer application</h2>
<p>The AWS Photo Analyzer application is an example application that uses the Amazon Rekognition service, other AWS services, and the AWS SDK for Java version 2.
Analyzing nature photographs has never been easier! Just perform these steps:<p>
<ol>
<li>Upload a nature photograph to an Amazon S3 bucket by choosing the <b>Upload Photos</b> menu item.</li>
<li>Choose <b>Choose File</b> and browse to a nature image located on your desktop.</li>
<li>Choose <b>Upload</b> to upload your image to an S3 bucket.</li>
<li>Choose <b>Get Images</b> to view the images located in the S3 bucket. All images in the bucket are displayed in the table. </li>
<li>Analyze the photographs and produce a report by choosing the <b>Analyze Photos</b> menu item. </li>
<li>Enter an email address in the email field and choose <b>Analyze Photos</b>. </li>
<li>Amazon SES is used to send an email with an Excel report to the specified email recipient.</li>
</ol>
</div>
</body>
</html>
The following HTML represents the process.html file.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script th:src="|https://code.jquery.com/jquery-1.12.4.min.js|"></script>
<script th:src="|https://code.jquery.com/ui/1.11.4/jquery-ui.min.js|"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"/>
<script src="../public/js/message.js" th:src="@{/js/message.js}"></script>
<link rel="stylesheet" href="../public/css/styles.css" th:href="@{/css/styles.css}" />
<link rel="icon" href="../public/images/favicon.ico" th:href="@{/images/favicon.ico}" />
<title>AWS Photo Analyzer</title>
<script>
function myFunction() {
alert("The form was submitted");
}
</script>
</head>
<body>
<header th:replace="layout :: site-header"/>
<div class="container">
<h2>AWS Photo Analyzer Application</h2>
<p>You can generate a report that analyzes the images in the S3 bucket. You can send the report to the following email address. </p>
<label for="email">Email address:</label><br>
<input type="text" id="email" name="email" value=""><br>
<div>
<br>
<p>Click the following button to obtain a report</p>
<button onclick="ProcessImages()">Analyze Photos</button>
</div>
<div id ="bar" class="progress">
<div class="progress-bar progress-bar-striped active" role="progressbar"
aria-valuenow="40" aria-valuemin="0" aria-valuemax="100" style="width:90%">
Generating Report
</div>
</div>
<div>
<h3>Download a photo to your browser</h3>
<p>Specify the photo to download from an Amazon S3 bucket</p>
<label for="photo">Photo Name:"</label><br>
<input type="text" id="photo" name="photo" value=""><br>
<p>Click the following button to download a photo</p>
<button onclick="DownloadImage()">Download Photo</button>
</div>
</div>
</body>
</html>
The following HTML represents the upload.html file.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script th:src="|https://code.jquery.com/jquery-1.12.4.min.js|"></script>
<script th:src="|https://code.jquery.com/ui/1.11.4/jquery-ui.min.js|"></script>
<script th:src="|https://cdn.datatables.net/v/dt/dt-1.10.20/datatables.min.js|"></script>
<script src="../public/js/items.js" th:src="@{/js/items.js}"></script>
<link rel="stylesheet" th:href="|https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css|"/>
<link rel="stylesheet" th:href="|https://cdn.datatables.net/v/dt/dt-1.10.20/datatables.min.css|"/>
<link rel="stylesheet" href="../public/css/styles.css" th:href="@{/css/styles.css}" />
<link rel="icon" href="../public/images/favicon.ico" th:href="@{/images/favicon.ico}" />
<title>AWS Photo Analyzer</title>
<script>
function myFunction() {
alert("The form was submitted");
}
</script>
</head>
<body>
<header th:replace="layout :: site-header"/>
<div class="container">
<h2>AWS Photo Analyzer application</h2>
<p>Upload images to an Amazon S3 bucket. Each image will be analyzed!</p>
<form method="POST" onsubmit="myFunction()" action="/upload" enctype="multipart/form-data">
<input type="file" name="file" /><br/><br/>
<input type="submit" value="Submit" />
</form>
<div>
<br>
<p>Choose the following button to determine the number of images in the bucket.</p>
<button onclick="getImages()">Get Images</button>
<table id="myTable" class="display" style="width:100%">
<thead>
<tr>
<th>Name</th>
<th>Owner</th>
<th>Date</th>
<th>Size</th>
</tr>
</thead>
<tbody>
<tr>
<td>No Data</td>
<td>No Data</td>
<td>No Data </td>
<td>No Data</td>
</tr>
</tbody>
<tfoot>
<tr>
<th>Name</th>
<th>Owner</th>
<th>Date</th>
<th>Size</th>
</tr>
</tfoot>
<div id="success3"></div>
</table>
</div>
</div>
</body>
</html>
The following HTML represents the layout.html file for the application's menu.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="site-head">
<meta charset="UTF-8" />
<link rel="icon" href="../public/images/favicon.ico" th:href="@{/images/favicon.ico}" />
<script th:src="|https://code.jquery.com/jquery-1.12.4.min.js|"></script>
<meta th:include="this :: head" th:remove="tag"/>
</head>
<body>
<!-- th:hef calls a controller method - which returns the view -->
<header th:fragment="site-header">
<a href="#" style="color: white" th:href="@{/}">Home</a>
<a href="#" style="color: white" th:href="@{/photo}">Upload Photos</a>
<a href="#" style="color: white" th:href="@{/process}">Analyze Photos</a>
</header>
</html>
Both the upload and process views use script files to communicate with the Spring controller. You have to ensure that these files are part of your project; otherwise, your application won't work.
- items.js
- message.js
Both files contain application logic that sends a request to the Spring controller. In addition, these files handle the response and set the data in the view.
The following JavaScript represents the items.js file.
$(function() {
$('#myTable').DataTable( {
scrollY: "500px",
scrollX: true,
scrollCollapse: true,
paging: true,
columnDefs: [
{ width: 200, targets: 0 }
],
fixedColumns: true
} );
} );
function getImages() {
$.ajax('/getimages', {
type: 'GET', // http method
success: function (data, status, xhr) {
var xml = data
var oTable = $('#myTable').dataTable();
oTable.fnClearTable(true);
$(xml).find('Item').each(function () {
var $field = $(this);
var key = $field.find('Key').text();
var name = $field.find('Owner').text();
var date = $field.find('Date').text();
var size = $field.find('Size').text();
//Set the new data
oTable.fnAddData( [
key,
name,
date,
size]
);
});
},
});
}
The following JavaScript represents the message.js file. The ProcessImages function sends a request to the /report handler in the controller that generates a report. Notice that an email address is posted to the Controller method.
$(function() {
$("#bar").hide()
} );
function ProcessImages() {
//Post the values to the controller
$("#bar").show()
var email = $('#email').val();
$.ajax('/report', {
type: 'POST', // http method
data: 'email=' + email , // data to submit
success: function (data, status, xhr) {
$("#bar").hide()
alert(data) ;
},
error: function (jqXhr, textStatus, errorMessage) {
$('p').append('Error' + errorMessage);
}
});
}
function DownloadImage(){
//Post the values to the controller
var photo = $('#photo').val();
window.location="../downloadphoto?photoKey=" + photo ;
}
Note: There are other CSS files located in the GitHub repository that you must add to your project. Ensure all of the files under the resources folder are included in your project.
Using the IntelliJ IDE, you can run your application. The first time you run the Spring Boot application, click the run icon in the Spring Boot main class, as shown in this illustration.
Note: You can deploy this Spring Boot application by using AWS Elastic Beanstalk. For information, see the following document Creating your first AWS Java web application.
Congratulations! You have created and deployed the AWS Photo Analyzer application. As stated at the beginning of this tutorial, be sure to terminate all of the resources you create while going through this tutorial to ensure that you’re no longer charged for them.
For more AWS multiservice examples, see usecases.