news 2026/9/25 18:49:10

AWS Photo Analyzer:基于 AWS SDK for Java V2 异步客户端构建 Spring Boot 照片分析 Web 应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AWS Photo Analyzer:基于 AWS SDK for Java V2 异步客户端构建 Spring Boot 照片分析 Web 应用
  • 示例工程
  • 教程
  • 后端

【免费下载链接】aws-doc-sdk-examples

Welcome to the AWS Code Examples Repository. This repo contains code examples used in the AWS documentation, AWS SDK Developer Guides, and more. For more information, see the Readme.md file below.

项目地址:https://gitcode.com/gh_mirrors/aw/aws-doc-sdk-examples
点击查看免费下载

本指南完整讲解如何在aws-doc-sdk-examples仓库的javav2/usecases/creating_photo_analyzer_async示例基础上,从零构建名为AWS Photo Analyzer的 Spring Boot 动态 Web 应用。该应用以 AWS SDK for Java V2 的异步客户端(S3AsyncClient、RekognitionAsyncClient)为核心,实现照片上传、批量标签识别、Excel 报告生成与邮件发送的完整链路。读完本文,你将掌握异步客户端的CompletableFuture+AtomicReference编程模式、各 AWS 服务在 Spring Boot 中的装配方式,以及如何运行和部署这套完整的多服务用例。

应用概览与整体架构

项目说明
示例名称AWS Photo Analyzer(异步版)
源码位置javav2/usecases/creating_photo_analyzer_async
目标读者初中级开发者
前置技能Java、Maven
涉及 AWS 服务Amazon Rekognition、Amazon S3、Amazon SES
关键技术AWS SDK for Java V2异步客户端、Spring Boot MVC、Thymeleaf、JXL 报表

核心工作流程

  1. 用户通过 Web 界面将自然风景图片上传到 Amazon S3 桶(/upload接口,S3AsyncClient.putObject);
  2. 页面通过/getimages接口读取桶内全部对象,并以 XML 形式回传(S3AsyncClient.listObjects);
  3. 用户在/process页面输入收件邮箱并点击Analyze Photos,控制器/report遍历桶内每个对象,读取图片字节后调用 Amazon Rekognition 的detectLabels对每张图做标签识别;
  4. 识别结果(照片、标签、置信度)交给 JXL API 生成多 Sheet 的 Excel 报告;
  5. 报告通过 Amazon SES 的sendRawEmail作为附件发送到指定邮箱;
  6. 用户还可通过/downloadphoto把桶内图片下载到浏览器。

整个示例的关键设计是:S3 与 Rekognition 均使用 SDK V2 的异步客户端,SES 仍使用同步的SesClient。如果希望对照使用同步客户端的同主题用例,可参阅仓库中姊妹项目 creating_photo_analyzer_app。

前置条件与资源准备

环境要求

  • 一个可用的 AWS 账户;
  • Java IDE(本教程以 IntelliJ IDEA 为例);
  • Java JDK 17;
  • Maven 3.6 或更高版本。

提示:文中涉及的 AWS 服务均包含在 AWS Free Tier 中;但并非所有服务在所有区域可用,代码运行可能产生费用,请在使用后及时终止相关资源以免持续计费。

创建 S3 桶

创建一个名为photos[somevalue]的 S3 桶(例如photos2026),并确保将桶名同步配置到PhotoController的bucketName字段中。同时按 AWS 官方指引完成开发环境配置,使 SDK 能通过环境变量获取凭证——本示例中AnalyzePhotos与S3Service均使用EnvironmentVariableCredentialsProvider.create()(源码见 AnalyzePhotos.java 与 S3Service.java)。

创建 IntelliJ Maven 项目

  1. 在 IntelliJ 中选择File → New → Project;
  2. 在New Project对话框中选择Maven,点击Next;
  3. GroupId填写aws-spring;
  4. ArtifactId填写SpringPhotoAnalyzerAsync;
  5. 点击Next与Finish完成创建。

项目骨架建立后,按仓库中的 pom.xml 配置依赖。

POM 依赖配置详解

以下是本示例使用的完整依赖清单(来自仓库 pom.xml):

依赖作用
spring-boot-starter-parent2.6.1Spring Boot 父 POM,统一管理版本
spring-boot-starter-thymeleaf模板引擎,渲染 HTML 视图
spring-boot-starter-webSpring MVC Web 能力
software.amazon.awssdk:bom2.21.20AWS SDK for Java V2 依赖清单(BOM,统一版本)
software.amazon.awssdk:rekognitionAmazon Rekognition 客户端(含异步)
software.amazon.awssdk:s3Amazon S3 客户端(含异步)
software.amazon.awssdk:sesAmazon SES 客户端
net.sourceforge.jexcelapi:jxl2.6.12JXL API,生成 Excel 报告(非 AWS 服务)
commons-io:commons-io2.7字节流/输入输出工具
javax.mail:javax.mail-api、com.sun.mail:javax.mail1.6.2构建 MIME 邮件消息
spring-boot-starter-test测试依赖(scope=test)

关键配置点:

  • <java.version>17</java.version>与maven.compiler.target/source均设为 17,务必使用 Java 17 运行;
  • AWS SDK 依赖通过dependencyManagement引入bom(版本 2.21.20)统一管理,具体服务 artifact 无需写版本号;
  • 构建插件使用spring-boot-maven-plugin,可直接以可执行 JAR 方式运行应用。

Java 类设计与源码实现

在main/java下创建包com.example.photo,共 8 个类:

类职责
AnalyzePhotos用 Rekognition 异步客户端识别图片标签
BucketItem模型:S3 对象信息(key/owner/date/size)
PhotoApplicationSpring Boot 启动入口
PhotoControllerMVC 控制器,处理全部 HTTP 请求
SendMessages用 SES 发送带附件的邮件
S3Service用 S3 异步客户端完成对象操作
WorkItem模型:Rekognition 标签数据(key/name/confidence)
WriteExcel用 JXL API 动态生成 Excel 报告

核心异步模式:CompletableFuture + AtomicReference

异步客户端 API 的特点是方法立即返回CompletableFuture,结果在回调中可用。以AnalyzePhotos.DetectLabels(见 AnalyzePhotos.java)为例,完整调用链如下:

RekognitionAsyncClient rekAsyncClient = RekognitionAsyncClient.builder() .credentialsProvider(EnvironmentVariableCredentialsProvider.create()) .region(Region.US_EAST_2) .build(); final AtomicReference<ArrayList<WorkItem>> reference = new AtomicReference<>(); SdkBytes sourceBytes = SdkBytes.fromByteArray(bytes); Image souImage = Image.builder().bytes(sourceBytes).build(); DetectLabelsRequest detectLabelsRequest = DetectLabelsRequest.builder() .image(souImage) .maxLabels(10) // 最多返回 10 个标签 .build(); CompletableFuture<DetectLabelsResponse> futureGet = rekAsyncClient.detectLabels(detectLabelsRequest); futureGet.whenComplete((resp, err) -> { if (resp != null) { List<Label> labels = resp.labels(); ArrayList<WorkItem> list = new ArrayList<>(); for (Label label : labels) { WorkItem item = new WorkItem(); item.setKey(key); // 标识照片 item.setConfidence(label.confidence().toString()); item.setName(label.name()); list.add(item); } reference.set(list); } else { err.printStackTrace(); } }); futureGet.join(); return reference.get();

这套代码演示了三个关键知识点:

  1. CompletableFuture.whenComplete:注册回调,通过resp对象获取服务端数据。例如resp.labels()返回List<Label>,每个Label可取出标签名(name())与置信度(confidence());
  2. AtomicReference跨越回调传值:无法直接从whenComplete回调内部return数据(会引发编译错误),因此用AtomicReference在回调内set结果,再在回调外get取出并作为方法返回值;源码中futureGet.join()保证异步任务完成后才读取引用;
  3. rekAsyncClient.close():放在finally块中,确保客户端在完全用完后再关闭。

S3Service采用完全相同的模式实现三个方法(见 S3Service.java):

  • getObjectBytes(bucketName, keyName):构造GetObjectRequest,配合AsyncResponseTransformer.toBytes()调用s3AsyncClient.getObject(...),在回调中reference.set(resp.asByteArray())得到图片字节数组;
  • ListBucketObjects(bucketName):调用s3AsyncClient.listObjects(...),遍历resp.contents()收集全部对象的 key;
  • putObject(data, bucketName, objectKey):用AsyncRequestBody.fromBytes(data)作为请求体调用s3AsyncClient.putObject(...)完成上传。

注意S3Service构造客户端时区域为Region.US_WEST_2(S3Service.java#L44-L49),而AnalyzePhotos使用Region.US_EAST_2(AnalyzePhotos.java#L26),运行时请按你实际使用的区域调整。

PhotoController:HTTP 请求与业务编排

PhotoController(见 PhotoController.java)通过构造器注入S3Service、AnalyzePhotos、WriteExcel、SendMessages四个组件,映射了 6 个端点:

端点与方法功能
GET /→root()返回首页index
GET /process→process()返回报告生成页process
GET /photo→photo()返回上传页upload
POST /report→report()遍历桶内图片→识别标签→生成 Excel→发邮件
POST /upload→singleFileUpload()接收MultipartFile,写入 S3 桶
GET /getimages→getImages()返回桶内对象 XML
GET /downloadphoto→buildDynamicReportDownload()下载指定照片到浏览器

其中/report是核心编排方法:

String email = request.getParameter("email"); ArrayList<String> myKeys = (ArrayList<String>) s3Service.ListBucketObjects(bucketName); ArrayList<List<WorkItem>> myList = new ArrayList<>(); for (String myKey : myKeys) { byte[] keyData = s3Service.getObjectBytes(bucketName, myKey); ArrayList<WorkItem> item = photos.DetectLabels(keyData, myKey); myList.add(item); } InputStream excelData = excel.exportExcel(myList); sendMessage.sendReport(excelData, email);

/downloadphoto方法则通过response.setContentType("image/png")与Content-disposition: attachment; filename=...响应头,配合IOUtils.copy将图片字节写入输出流,实现浏览器直接下载。

务必修改的配置:将bucketName字段替换为你的 S3 桶名(PhotoController.java#L23中默认值为占位符<Enter your S3 bucket name>)。

模型类:BucketItem 与 WorkItem

  • BucketItem:封装key、owner、date、size四个字段,用于在/getimages视图渲染 S3 对象元数据;
  • WorkItem:封装key、name、confidence三个字段,key标识照片,name是 Rekognition 返回的标签名,confidence为置信度字符串,作为 Excel 报告的数据载体。

WriteExcel:用 JXL 生成多 Sheet 报告

WriteExcel(见 WriteExcel.java)使用 JXL API(net.sourceforge.jexcelapi:jxl,非 AWS 服务)将List<List<WorkItem>>写成内存中的WritableWorkbook:

  • 外层列表中的每个元素(一张照片的标签集合)对应一个名为Sheet N的工作表(WriteExcel.java#L47-L54);
  • 每张表表头固定为Photo / Label / Confidence三列(createLabel方法),使用带下划线的粗体 Times 字体并开启自动换行;
  • createContent逐行写入key、name、confidence,列宽按内容字符数自动伸缩(超过 200 字符时固定为 150 列宽);
  • 最终workbook.write()后转为ByteArrayInputStream返回,供邮件发送与浏览器展示使用。

SendMessages:SES 发送带附件邮件

SendMessages(见 SendMessages.java)使用标准 JavaMail 构建多部分 MIME 消息,再通过 SESsendRawEmail发送:

  • 先构建multipart/alternative子容器(同时携带纯文本与 HTML 正文),再包入multipart/mixed父容器,附加 Excel 附件;
  • 附件通过ByteArrayDataSource(attachment, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")承载,文件名为PhotoReport.xls(SendMessages.java#L92-L98);
  • 将MimeMessage序列化为字节后包装为SdkBytes,构造RawMessage与SendRawEmailRequest,调用client.sendRawEmail(...)发送;
  • 注意源码中sender为<Enter a validated email address>占位符,且 SES 发送方地址必须在 SES 控制台中完成验证。

HTML 视图与脚本文件

在src/main/resources下创建templates目录,放置 4 个 Thymeleaf 模板(仓库源码对应位置见 creating_photo_analyzer_async 目录结构):

文件视图角色
index.html首页,介绍应用与使用步骤
process.html输入邮箱生成报告、下载照片
upload.html上传图片、查看桶内对象表格
layout.html全站共享导航菜单(Home / Upload Photos / Analyze Photos)

三个页面均通过<header th:replace="layout :: site-header"/>引入共享菜单,并引用 Bootstrap、jQuery、DataTables 等 CDN 资源;upload.html中<form method="POST" action="/upload" enctype="multipart/form-data">直连控制器的/upload端点,process.html提供邮箱输入框与Analyze Photos / Download Photo两个按钮。

还需要在resources下加入两个 JS 脚本(否则页面无法与控制器通信):

  • items.js:getImages()通过$.ajax('/getimages', { type: 'GET' })拉取 XML,解析每个<Item>节点的 Key/Owner/Date/Size,用 DataTables 的fnAddData填充表格;
  • message.js:ProcessImages()通过$.ajax('/report', { type: 'POST', data: 'email=' + email })提交邮箱并显示进度条;DownloadImage()跳转至/downloadphoto?photoKey=...触发下载。

注意:仓库中resources目录还包含其他 CSS 等静态资源文件,必须一并加入项目,否则页面样式与交互会缺失。

运行与部署

在 IntelliJ 中直接运行PhotoApplication主类(点击运行图标,见仓库示意图 run.png),Spring Boot 应用即在本机启动;随后按首页指引依次完成上传照片 → 查看图片列表 → 输入邮箱生成报告 → 查收邮件附件的完整流程。

部署方面,该 Spring Boot 应用可打包后部署到 AWS Elastic Beanstalk,具体可参考仓库中姊妹教程 creating_first_project。

后续步骤

恭喜你完成了 AWS Photo Analyzer 异步版应用的构建与运行!再次提醒:请务必终止本教程中创建的 S3 桶等所有资源,避免持续产生 AWS 费用。如需查看更多多服务用例,可浏览仓库的 javav2/usecases 目录。

  • 示例工程
  • 教程
  • 后端

【免费下载链接】aws-doc-sdk-examples

Welcome to the AWS Code Examples Repository. This repo contains code examples used in the AWS documentation, AWS SDK Developer Guides, and more. For more information, see the Readme.md file below.

项目地址:https://gitcode.com/gh_mirrors/aw/aws-doc-sdk-examples
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 18:44:10

Atlas 300V 24G部署YOLO实战:从环境配置到模型推理优化

1. Atlas 300V 24G&#xff1a;先搞清楚它算不算是“运算加速卡”最近在团队里接了一台新设备&#xff0c;原话就只有一句&#xff1a;“给它装上Atlas&#xff0c;跑YOLO。”我第一反应是&#xff1a;哪个Atlas&#xff1f;等把设备拿到手&#xff0c;翻过标签确认是Atlas 300…

作者头像 李华
网站建设 2026/9/25 18:41:28

智能体工作流工程化:从8000个AI Bot看低代码规模化生产

1. 项目本质还原&#xff1a;这不是“造AI”&#xff0c;而是构建可复用的智能体工作流“央视点赞&#xff01;南开大学10天造了8000个AI智能体”——这个标题在社交平台刷屏时&#xff0c;我第一反应不是兴奋&#xff0c;而是皱眉。作为带过三届AI工程实践课的从业者&#xff…

作者头像 李华
网站建设 2026/9/25 18:40:28

第22节 构造函数与初始化列表:解决const、引用成员初始化坑点

承接上文&#xff1a;上一节学习了类的成员变量、成员函数与工程编码规范&#xff0c;我们发现对象创建后成员变量默认是随机垃圾值&#xff0c;需要手动调用setter赋值。本节学习构造函数&#xff0c;对象创建时自动执行的特殊成员函数&#xff1b;再深入初始化列表&#xff0…

作者头像 李华
网站建设 2026/9/25 18:40:00

CPU底层原理:一条指令从取指到多核调度的完整链路

CPU 到底是怎么把程序跑起来的&#xff0c;很多人能背出“取指、译码、执行”六个字&#xff0c;但真到 CPU 跑满、缓存未命中、多核调度、天梯图选购的时候&#xff0c;又会开始凭感觉。这篇文章不铺垫背景&#xff0c;直接沿着一条指令从软件到硬件、从启动到完成的主线&…

作者头像 李华
网站建设 2026/9/25 18:37:38

轻松学习Zephyr BSP: 46 — BSP Security SBOM

摘要:本文系统讲解 BSP(板级支持包)的安全体系与 SBOM(软件物料清单)实践。文章从 BSP 安全整体认识出发,说明 BSP 安全不是简单的安全宏,而是覆盖 Source、Build、Runtime 的完整链路;随后深入介绍 SBOM 的定义、价值与常见格式(SPDX/CycloneDX),强调 BSP 因生命周…

作者头像 李华