kotlin/usecases/creating_photo_analyzer_app/README.md
You can create a dynamic web application that analyzes nature images located in an Amazon Simple Storage Service (Amazon S3) bucket by using the Amazon Rekognition service and the AWS SDK for Kotlin. The web application can analyze many images and generate data that breaks down each image into a series of labels. For example, the following image shows a lake during sun rise.
After the application analyzes this image, it creates this data:
In this tutorial, you create a Spring Boot application named Kotlin Photo Analyzer. The Spring Boot APIs are used to build a model, different views, and a controller. For more information, see Spring Boot Tutorial.
This application uses the following AWS services:
To complete the tutorial, you need the following:
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.
The AWS Photo Analyzer application supports uploading images to an Amazon S3 bucket. After the images are uploaded, you can view the images to be analyzed.
To generate a report, choose the Analyze Photos menu option and then choose the Download Report button.
The following figure shows the report that is generated. Notice that image data is located in a separate sheet.
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.
Perform these steps.
At this point, you have a new project named PhotoAnalyzerKotlin.
Add the following dependencies to your Gradle buidle file.
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.9.0"
application
}
group = "me.scmacdon"
version = "1.0-SNAPSHOT"
buildscript {
repositories {
maven("https://plugins.gradle.org/m2/")
}
dependencies {
classpath("org.jlleitschuh.gradle:ktlint-gradle:11.5.1")
}
}
repositories {
mavenCentral()
}
apply(plugin = "org.jlleitschuh.gradle.ktlint")
dependencies {
implementation("aws.sdk.kotlin:s3:0.33.1-beta")
implementation("aws.sdk.kotlin:rekognition:0.33.1-beta")
implementation("aws.smithy.kotlin:http-client-engine-okhttp:0.28.0")
implementation("aws.smithy.kotlin:http-client-engine-crt:0.28.0")
implementation("org.springframework.boot:spring-boot-starter-web:2.7.4")
implementation("org.springframework.boot:spring-boot-starter-thymeleaf:2.7.4")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("net.sourceforge.jexcelapi:jxl:2.6.10")
implementation("commons-io:commons-io:2.10.0")
testImplementation("org.springframework.boot:spring-boot-starter-test:2.7.3")
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "17"
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
Create a package in the main/kotlin folder named com.aws.photo. The Kotlin files go into this package.
Create these Kotlin classes:
Note The MessageResource class is located in the PhotoApp file.
The following Kotlin code represents the AnalyzePhotos class that uses the Amazon Rekognition API to analyze the images.
package com.aws.photo
import aws.sdk.kotlin.services.rekognition.RekognitionClient
import aws.sdk.kotlin.services.rekognition.model.DetectLabelsRequest
import aws.sdk.kotlin.services.rekognition.model.Image
import org.springframework.stereotype.Component
@Component
class AnalyzePhotos {
suspend fun DetectLabels(bytesVal: ByteArray?, key: String?): MutableList<WorkItem>? {
// Create an Image object for the source image.
val souImage = Image {
bytes = bytesVal
}
val detectLabelsRequest = DetectLabelsRequest {
image = souImage
maxLabels = 10
}
RekognitionClient { region = "us-west-2" }.use { rekClient ->
val response = rekClient.detectLabels(detectLabelsRequest)
// Write the results to a WorkItem instance.
val list = mutableListOf<WorkItem>()
println("Detected labels for the given photo")
response.labels?.forEach { label ->
val item = WorkItem()
item.key = key // identifies the photo.
item.confidence = label.confidence.toString()
item.name = label.name
list.add(item)
}
return list
}
}
}
The following Java code represents the BucketItem class that stores S3 object data.
package com.aws.photo
class BucketItem {
var key: String? = null
var owner: String? = null
var date: String? = null
var size: String? = null
}
The following Java code represents the PhotoApplication class.
package com.aws.photo
import kotlinx.coroutines.runBlocking
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartFile
import org.springframework.web.servlet.ModelAndView
import org.springframework.web.servlet.view.RedirectView
import java.io.ByteArrayInputStream
import java.io.IOException
import java.io.InputStream
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
@SpringBootApplication
class PhotoApp
fun main(args: Array<String>) {
runApplication<PhotoApp>(*args)
}
@Controller
class MessageResource {
// Change to your Bucket Name.
private val bucketName = "<BUCKETNAME>"
@Autowired
var s3Service: S3Service? = null
@Autowired
var recService: AnalyzePhotos? = null
@Autowired
var excel: WriteExcel? = null
@GetMapping("/process")
fun process(): String? {
return "process"
}
@GetMapping("/photo")
fun photo(): String? {
return "upload"
}
@GetMapping("/")
fun root(): String? {
return "index"
}
@RequestMapping(value = ["/getimages"], method = [RequestMethod.GET])
@ResponseBody
fun getImages(request: HttpServletRequest?, response: HttpServletResponse?): String? = runBlocking{
return@runBlocking s3Service?.ListAllObjects(bucketName)
}
// Generates a report that analyzes photos in a given bucket.
@RequestMapping(value = ["/report"], method = [RequestMethod.GET])
@ResponseBody
fun report(request: HttpServletRequest, response: HttpServletResponse) = runBlocking {
// Get a list of key names in the given bucket.
val myKeys = s3Service?.listBucketObjects(bucketName)
// loop through each element in the List.
val myList = mutableListOf<List<*>>()
val len = myKeys?.size
for (z in 0 until len!!) {
val key = myKeys?.get(z) as String
val keyData = s3Service?.getObjectBytes(bucketName, key)
//Analyze the photo.
val item = recService?.DetectLabels(keyData, key)
if (item != null) {
myList.add(item)
}
}
// Now we have a list of WorkItems describing the photos in the S3 bucket.
val excelData = excel?.exportExcel(myList)
try {
// Download the report.
val reportName = "ExcelReport.xls"
response.contentType = "application/vnd.ms-excel"
response.setHeader("Content-disposition", "attachment; filename=$reportName")
org.apache.commons.io.IOUtils.copy(excelData, response?.outputStream)
response.flushBuffer()
} catch (e: Exception) {
e.printStackTrace()
}
}
// Downloads the given image from the Amazon S3 bucket.
@RequestMapping(value = ["/downloadphoto"], method = [RequestMethod.GET])
fun fileDownload(request: HttpServletRequest, response: HttpServletResponse) = runBlocking {
try {
val photoKey = request.getParameter("photoKey")
val photoBytes: ByteArray? = s3Service?.getObjectBytes(bucketName, photoKey)
val `is`: InputStream = ByteArrayInputStream(photoBytes)
// Define the required information here.
response.contentType = "image/png"
response.setHeader("Content-disposition", "attachment; filename=$photoKey")
org.apache.commons.io.IOUtils.copy(`is`, response.outputStream)
response.flushBuffer()
} catch (e: Exception) {
e.printStackTrace()
}
}
// Upload a photo to an Amazon S3 bucket.
@RequestMapping(value = ["/upload"], method = [RequestMethod.POST])
@ResponseBody
fun singleFileUpload(@RequestParam("file") file: MultipartFile): ModelAndView? = runBlocking {
try {
val bytes = file.bytes
val name = file.originalFilename
// Put the file into the bucket.
s3Service?.putObject(bytes, bucketName, name)
} catch (e: IOException) {
e.printStackTrace()
}
return@runBlocking ModelAndView(RedirectView("photo"))
}
}
Note: Be sure to replace the bucket name in this code example with your bucket name.
The following class uses the Amazon S3 Kotlin API to perform S3 operations. For example, the getObjectBytes method returns a byte array that represents the image.
package com.aws.photo
import aws.sdk.kotlin.services.s3.S3Client
import aws.sdk.kotlin.services.s3.model.*
import aws.smithy.kotlin.runtime.content.ByteStream
import aws.smithy.kotlin.runtime.content.toByteArray
import org.springframework.stereotype.Component
import org.w3c.dom.Document
import java.io.StringWriter
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.parsers.ParserConfigurationException
import javax.xml.transform.TransformerException
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult
import kotlin.system.exitProcess
@Component
class S3Service {
var myBytes: ByteArray? = null
// Returns the names of all images in the given bucket.
suspend fun listBucketObjects(bucketName: String?): List<*>? {
var keyName: String
val keys = mutableListOf<String>()
val listObjects = ListObjectsRequest {
bucket = bucketName
}
S3Client { region = "us-west-2" }.use { s3Client ->
val response = s3Client.listObjects(listObjects)
response.contents?.forEach { myObject ->
keyName = myObject.key.toString()
keys.add(keyName)
}
return keys
}
}
// Returns the names of all images and data within an XML document.
suspend fun ListAllObjects(bucketName: String?): String? {
var sizeLg: Long
var dateIn: aws.smithy.kotlin.runtime.time.Instant?
val bucketItems = mutableListOf<BucketItem>()
val listObjects = ListObjectsRequest {
bucket = bucketName
}
S3Client { region = "us-west-2" }.use { s3Client ->
val res = s3Client.listObjects(listObjects)
res.contents?.forEach { myObject ->
val myItem = BucketItem()
myItem.key = myObject.key
myItem.owner = myObject.owner?.displayName.toString()
sizeLg = (myObject.size / 1024)
myItem.size = (sizeLg.toString())
dateIn = myObject.lastModified
myItem.date = dateIn.toString()
// Push the items to the list.
bucketItems.add(myItem)
}
return convertToString(toXml(bucketItems))
}
}
// Places an image into an Amazon S3 bucket.
suspend fun putObject(data: ByteArray, bucketName: String?, objectKey: String?): String? {
val request = PutObjectRequest{
bucket = bucketName
key = objectKey
body = ByteStream.fromBytes(data)
}
S3Client { region = "us-west-2" }.use { s3Client ->
val response = s3Client.putObject(request)
return response.eTag
}
}
// Get the byte[] from this Amazon S3 object.
suspend fun getObjectBytes(bucketName: String?, keyName: String?): ByteArray? {
val objectRequest = GetObjectRequest {
key = keyName
bucket = bucketName
}
S3Client { region = "us-west-2" }.use { s3Client ->
s3Client.getObject(objectRequest) { resp ->
myBytes = resp.body?.toByteArray()
}
return myBytes
}
}
// Convert items into XML to pass back to the view.
private fun toXml(itemList: List<BucketItem>): Document {
try {
val factory = DocumentBuilderFactory.newInstance()
val builder = factory.newDocumentBuilder()
val doc = builder.newDocument()
// Start building the XML.
val root = doc.createElement("Items")
doc.appendChild(root)
// Get the elements from the collection.
val custCount = itemList.size
// Iterate through the collection.
for (index in 0 until custCount) {
// Get the WorkItem object from the collection.
val myItem = itemList[index]
val item = doc.createElement("Item")
root.appendChild(item)
// Set Key.
val id = doc.createElement("Key")
id.appendChild(doc.createTextNode(myItem.key))
item.appendChild(id)
// Set Owner.
val name = doc.createElement("Owner")
name.appendChild(doc.createTextNode(myItem.owner))
item.appendChild(name)
// Set Date.
val date = doc.createElement("Date")
date.appendChild(doc.createTextNode(myItem.date))
item.appendChild(date)
// Set Size.
val desc = doc.createElement("Size")
desc.appendChild(doc.createTextNode(myItem.size))
item.appendChild(desc)
}
return doc
} catch (e: ParserConfigurationException) {
e.printStackTrace()
exitProcess(0)
}
}
private fun convertToString(xml: Document): String {
try {
val transformer = TransformerFactory.newInstance().newTransformer()
val result = StreamResult(StringWriter())
val source = DOMSource(xml)
transformer.transform(source, result)
return result.writer.toString()
} catch (ex: TransformerException) {
ex.printStackTrace()
exitProcess(0)
}
}
}
The following Kotlin code represents the WorkItem class.
package com.aws.photo
class WorkItem {
var key: String? = null
var name: String? = null
var confidence: String? = null
}
The following Kotlin code represents the WriteExcel class.
package com.aws.photo
import jxl.CellView
import jxl.Workbook
import jxl.WorkbookSettings
import jxl.format.UnderlineStyle
import jxl.write.*
import jxl.write.Number
import org.springframework.stereotype.Component
import java.io.*
import java.util.*
@Component
class WriteExcel {
private var timesBoldUnderline: WritableCellFormat? = null
private var times: WritableCellFormat? = null
// Returns an InputStream that represents the Excel Report.
fun exportExcel(list: List<List<*>>): InputStream? {
try {
return write(list)
} catch (e: WriteException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}
return null
}
// Generates the report and returns an inputstream.
@Throws(IOException::class, WriteException::class)
fun write(list: List<List<*>>): InputStream {
val os: OutputStream = ByteArrayOutputStream()
val wbSettings = WorkbookSettings()
wbSettings.locale = Locale("en", "EN")
// Create a Workbook - pass the OutputStream.
val workbook = Workbook.createWorkbook(os, wbSettings)
val size = list.size
for (i in 0 until size) {
// Get the WorkItem from each list.
val innerList = list[i]
val wi = innerList[i] as WorkItem
workbook.createSheet(wi.key.toString() + " Sheet ", 0)
val excelSheet = workbook.getSheet(0)
createLabel(excelSheet)
createContent(excelSheet, innerList)
}
// Close the workbook.
workbook.write()
workbook.close()
// Get an inputStram that represents the Report.
var stream = ByteArrayOutputStream()
stream = os as ByteArrayOutputStream
val myBytes = stream.toByteArray()
return ByteArrayInputStream(myBytes)
}
// Create Headings in the Excel spreadsheet.
@Throws(WriteException::class)
private fun createLabel(sheet: WritableSheet) {
// Create a times font.
val times10pt = WritableFont(WritableFont.TIMES, 10)
// Define the cell format.
times = WritableCellFormat(times10pt)
// Lets automatically wrap the cells.
times!!.wrap = true
// Create create a bold font with unterlines.
val times10ptBoldUnderline = WritableFont(
WritableFont.TIMES, 10, WritableFont.BOLD, false,
UnderlineStyle.SINGLE
)
timesBoldUnderline = WritableCellFormat(times10ptBoldUnderline)
// Lets automatically wrap the cells.
timesBoldUnderline!!.wrap = true
val cv = CellView()
cv.format = times
cv.format = timesBoldUnderline
cv.isAutosize = true
// Write a few headers.
addCaption(sheet, 0, 0, "Photo")
addCaption(sheet, 1, 0, "Label")
addCaption(sheet, 2, 0, "Confidence")
}
// Write the Work Item Data to the Excel Report.
@Throws(WriteException::class)
private fun createContent(sheet: WritableSheet, list: List<*>): Int {
val size = list.size
for (i in 0 until size) {
val wi = list[i] as WorkItem
// Get tne work item values.
val key = wi.key
val label = wi.name
val confidence= wi.confidence
// First column.
addLabel(sheet, 0, i + 2, key.toString())
// Second column.
addLabel(sheet, 1, i + 2, label.toString())
// Third column.
addLabel(sheet, 2, i + 2, confidence.toString())
}
return size
}
@Throws(WriteException::class)
private fun addCaption(sheet: WritableSheet, column: Int, row: Int, s: String) {
val label: Label
label = Label(column, row, s, timesBoldUnderline)
val cc = countString(s)
sheet.setColumnView(column, cc)
sheet.addCell(label)
}
@Throws(WriteException::class)
private fun addNumber(
sheet: WritableSheet, column: Int, row: Int,
integer: Int
) {
val number: Number
number = Number(column, row, integer.toDouble(), times)
sheet.addCell(number)
}
@Throws(WriteException::class)
private fun addLabel(sheet: WritableSheet, column: Int, row: Int, s: String) {
val label: Label
label = Label(column, row, s, times)
val cc = countString(s)
if (cc > 200) sheet.setColumnView(column, 150) else sheet.setColumnView(column, cc + 6)
sheet.addCell(label)
}
private fun countString(ss: String): Int {
var count = 0
// Counts each character except space.
for (i in 0 until ss.length) {
if (ss[i] != ' ') count++
}
return count
}
}
At this point, you have created all of the Kotlin files required for the AWS Kotlin 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 templates folder, and then create the following HTML files:
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 Amazon 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/4.5.2/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>
<script th:src="|https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.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://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}" />
<title>AWS Kotlin Photo Analyzer Application</title>
</head>
<body>
<header th:replace="layout :: site-header"/>
<div class="container">
<h2>AWS Kotlin Photo Analyzer Application</h2>
<p>The Kotlin Photo Analyzer sample application uses the Amazon Rekognition Service, the Amazon S3 Service, and the AWS SDK for Kotlin.
Analyzing nature photographs has never been easier! Simply perform these steps:<p>
<ol>
<li>You can upload a nature photograph to an Amazon S3 bucket by choosing the <i>Upload Photos</i> menu item.</li>
<li>Choose <i>Choose File</i> and browse to a nature image located on your local desktop.</li>
<li>Choose <i>Upload</i> to upload your image to an Amazon S3 bucket.</li>
<li>You can choose <i>Get Images</i> to view the images located in the Amazon S3 bucket. All images in the Amazon S3 bucket are displayed in the table. </li>
<li>You can analyze the photographs and produce a report by choosing the <i>Analyze Photos</i> menu item. </li>
<li>Download a Microsoft Excel report to your web browser that contains analyzed image data.</li>
<li>Download a specific image from an Amazon S3 bucket.</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" />
<link rel="stylesheet" th:href="|https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/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>
<script th:src="|https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js|"></script>
<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}" />
<title>Kotlin Photo Analyzer</title>
</head>
<body>
<header th:replace="layout :: site-header"/>
<div class="container">
<h2>AWS Kotlin Photo Analyzer Application</h2>
<p>You can download a report that analyzes the images in the S3 bucket. </p>
<div>
<h3>Download a report to your browser</h3>
<p>Click the following button to download an Excel report</p>
<button id="downBtn" onclick="DownloadReport()">Download Report</button>
</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>
<input type="text" id="photo" name="photo" value="">
<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" />
<link rel="stylesheet" th:href="|https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/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>
<script th:src="|https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.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://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}" />
<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 Kotlin Photo Analyzer Application</h2>
<p>Upload images to an Amazon S3 Bucket. Each image will be analysed!</p>
<form method="POST" onsubmit="myFunction()" action="/upload" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" value="Submit" />
</form>
<div>
<p>Click the following button to determine the number of images in the Amazon S3 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>
<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.
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 GET 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,,]
);
});
},
error: function (jqXhr, textStatus, errorMessage) {
$('p').append('Error' + errorMessage);
}
});
}
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() {
} );
function DownloadImage(e){
var photo = $('#photo').val();
window.location ="../downloadphoto?photoKey=" + photo ;
}
function DownloadReport(e){
window.location="../report" ;
}
This application uses a CSS file named styles.css file that is used for the menu.
body>header {
background: #000;
padding: 5px;
}
body>header>a>img, body>header a {
display: inline-block;
vertical-align: middle;
padding: 0px 5px;
font-size: 1.2em;
}
body>footer {
background: #eee;
padding: 5px;
margin: 10px 0;
text-align: center;
}
#logged-in-info {
float: right;
margin-top: 18px;
}
#logged-in-info form {
display: inline-block;
margin-right: 10px;
}
Using the IntelliJ IDE, you can run your application. The first time you run the Spring Boot application, you can run the application by clicking the run icon in the Spring Boot main class, as shown in this illustration.
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.