用 AWS SDK for Java 构建反馈情感分析器(FSA)多服务应用:从 Lambda 处理程序到 CDK 部署
示例工程教程后端【免费下载链接】aws-doc-sdk-examplesWelcome 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_fsa_app教程完整讲解如何使用 AWS SDK for Java (v2) 的异步客户端开发一个反馈情感分析器Feedback Sentiment AnalyzerFSA应用S3 接收用户上传的多语言评论图片经 EventBridge 触发 Step Functions 工作流依次调用 Amazon Textract、Comprehend、Translate、Polly 完成“OCR 提取 → 情感判定 → 英文翻译 → 语音合成”并把结果写入 DynamoDB 与 S3。读完后你将掌握多服务事件驱动工作流的编排方式、四个 AWS Lambda 处理程序Handlers与五个异步 Service 类的完整实现、Maven 依赖配置BOM、shade 插件以及通过 AWS CDK 一键部署整套基础设施的方法。一、应用目标与服务全景FSA 应用模拟一个酒店场景住客在评论卡上以法语等多种语言写下反馈酒店方希望通过自动化流水线提取文字、判断情感倾向、翻译成英文并生成可播放的语音。该应用完成以下六件事源自 README用 Amazon S3 存储桶托管静态网站资源并通过 Amazon CloudFront 分发站点通过 Amazon Cognito 为 Amazon API Gateway 提供认证访问API Gateway 把条目写入 S3 存储桶随后触发 Amazon EventBridge 规则启动 AWS Step Functions 工作流Step Functions 工作流编排 AWS Lambda、Amazon Textract、Amazon Comprehend、Amazon Translate 和 Amazon Polly 执行业务核心逻辑元数据存入 Amazon DynamoDB音频文件存入第 3 步提到的同一 S3 存储桶API Gateway 从 DynamoDB 读取元数据返回给前端。从源码结构看四个 Lambda 后端函数分别对应四个机器学习/媒体服务Lambda 函数使用的服务异步客户端职责ExtractTextAmazon TextractTextractAsyncClient从 S3 中的图片提取文本AnalyzeSentimentAmazon ComprehendComprehendAsyncClient检测文本语言与情感TranslateTextAmazon TranslateTranslateAsyncClient把文本翻译成英文SynthesizeAudioAmazon Polly S3 Transfer ManagerPollyAsyncClient、S3AsyncClientCRT合成 MP3 音频并上传 S3二、前置条件完成本教程需要一个 AWS 账号Java IDE教程使用 IntelliJJava JDK 17与仓库 pom.xml 中maven.compiler.source/target 17一致Maven 3.6 或更高版本。注意事项原文档 Important 一节文档涉及的 AWS 服务均包含在 AWS Free Tier 中该代码未在全部 AWS 区域测试部分服务只在特定区域可用源码中各客户端统一硬编码Region.US_EAST_1跨区域部署时需自行调整运行代码可能产生费用完成教程后务必删除所有资源需按 SDK 文档正确配置开发环境。所需 AWS 资源全部由仓库配套的 AWS CDK 脚本创建无需在 AWS 管理控制台中手动创建任何资源。三、理解 FSA 应用FSA 前端是一个基于 Cloudscape Design System 的 React 应用支持把包含文字的图片上传到 S3 存储桶文字可以是法语等各种语言的客户评论。用户通过 Amazon Cognito 认证后应用会展示所有已上传图片、翻译后的文字以及一个可以收听 Amazon Polly 生成音频的按钮。3.1 图片上传后的工作流图片进入存储桶后EventBridge 规则被触发并启动 Step Functions 工作流各步骤如下ExtractText从图片中提取文本可能是法语等其他语言AnalyzeSentiment获取文本的情感例如判定正面或负面ContinueIfPositive根据情感值路由工作流——若为正面Positive进入下一步 TranslateTextTranslateText把文本翻译成英文SynthesizeAudio把英文文本转换成 MP3 音频文件并放入 S3 存储桶PutPositiveComment把数据写入 Amazon DynamoDB 表。注意客户端 React 应用在工作流成功完成、数据写入 S3 与 DynamoDB 之后才会显示任何数据。该 DynamoDB 表由运行 AWS CDK 脚本时自动创建详见 feedback_sentiment_analyzer 的 CDK 说明。3.2 AWS Lambda 函数命名约定后端由上述四个 Lambda 函数实现使用 AWS SDK for Java v2。注意这些是短名称控制台中显示的完整名称取决于 CDK 配置格式为{NAME}{Function Name}例如fsa-user-java-SynthesizeAudio134971D4-x8Q5178Y4ZBH。这些 Lambda 无需手动部署全部由 CDK 脚本完成。四、创建 IntelliJ 项目在 IntelliJ IDE 中依次选择File、New、Project在New Project对话框中选择Maven然后选择NextGroupId填写aws-fsaArtifactId填写fsa_app选择Next再选择Finish。五、添加 POM 依赖README 要求项目pom.xml与仓库中的 POM 保持一致。仓库实际文件为 pom.xml其要点如下通过dependencyManagement导入software.amazon.awssdk:bom:2.21.20与org.apache.logging.log4j:log4j-bom:2.19.0两个 BOM统一各 SDK 模块版本SDK 模块依赖包括textract、comprehend、translate、polly、s3、s3control、dynamodb、lambda以及apache-client排除了commons-logging以消除日志冲突software.amazon.awssdk.crt:aws-crt:0.25.1为S3AsyncClient.crtBuilder()提供基于 AWS CRT 的异步 S3 客户端software.amazon.awssdk:s3-transfer-manager:2.20.26S3 Transfer Manager用于高吞吐上传com.amazonaws:aws-lambda-java-core:1.2.2与com.amazonaws:aws-lambda-java-events:3.11.1Lambda Java 运行时 APIRequestHandler、Context与事件模型com.googlecode.json-simple:json-simple:1.1Handlers 与 Services 间以org.json.simple.JSONObject传递结果构建插件maven-shade-plugin:3.3.0在package阶段执行shade把所有依赖打进单个 fat JAR——这正是 Lambda 部署包能“一个 JAR 走天下”的原因maven-surefire-plugin:2.22.2负责运行测试测试依赖junit-jupiter-api/junit-jupiter-engine5.9.0scope 为test。六、创建 Java 类在main/java下创建包com.example.fsa代码放入其子包。6.1 Handlers 包四个 Lambda 处理程序包com.example.fsa.handlers中的每个类都是一个独立 Lambda 函数的处理程序使用 AWS Lambda Java 运行时 API 的RequestHandler接口实现输入类型均为MapString, ObjectStep Functions 传入的 JSON 载荷通过context.getLogger().log(...)输出 CloudWatch 日志。AnalyzeSentimentHandler接收source_text字段调用情感服务后返回含sentiment与language_code的 JSON对应源码 AnalyzeSentimentHandler.javapackage com.example.fsa.handlers; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.example.fsa.services.DetectSentimentService; import org.json.simple.JSONObject; import java.util.Map; public class AnalyzeSentimentHandler implements RequestHandlerMapString, Object, JSONObject { Override public JSONObject handleRequest(MapString, Object requestObject, Context context) { String sourceText (String) requestObject.get(source_text); context.getLogger().log(Extracted text: sourceText); DetectSentimentService detectSentimentService new DetectSentimentService(); JSONObject jsonOb detectSentimentService.detectSentiments(sourceText); context.getLogger().log(JSON: jsonOb.toJSONString()); return jsonOb; } }ExtractTextHandler从 EventBridge 事件载荷中取出bucket与object两个字段调用 Textract 服务提取文本对应 ExtractTextHandler.javapackage com.example.fsa.handlers; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.example.fsa.services.ExtractTextService; import java.util.Map; public class ExtractTextHandler implements RequestHandlerMapString, Object, String{ Override public String handleRequest(MapString, Object requestObject, Context context) { // Get the Amazon Simple Storage Service (Amazon S3) bucket and object key from the Amazon EventBridge event. ExtractTextService textService new ExtractTextService(); String bucket (String) requestObject.get(bucket); String fileName (String) requestObject.get(object); context.getLogger().log(*** Bucket: bucket , fileName: fileName); String extractedText textService.getCardText(bucket, fileName); context.getLogger().log(*** Text: extractedText); return extractedText; } }SynthesizeAudioHandler读取translated_text、bucket、object在 key 后追加.mp3先用 Polly 合成音频再上传 S3对应 SynthesizeAudioHandler.javapackage com.example.fsa.handlers; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.example.fsa.services.PollyService; import com.example.fsa.services.S3Service; import java.io.IOException; import java.io.InputStream; import java.util.Map; public class SynthesizeAudioHandler implements RequestHandlerMapString, Object, String { Override public String handleRequest(MapString, Object requestObject, Context context) { S3Service s3Service new S3Service(); PollyService pollyService new PollyService(); String translatedText (String) requestObject.get(translated_text); String bucket (String) requestObject.get(bucket); String key (String) requestObject.get(object); key key .mp3; // Appends .mp3 to the existing value of key context.getLogger().log(*** Translated Text: translatedText and new key is key); try { InputStream is pollyService.synthesize(translatedText); String audioFile s3Service.putAudio(is, bucket, key); context.getLogger().log(You have successfully added the audioFile in bucket); return audioFile ; } catch (IOException e) { throw new RuntimeException(e); } } }TranslateTextHandler读取extracted_text与source_language_code翻译后以{translated_text: ...}形式返回对应 TranslateTextHandler.javapackage com.example.fsa.handlers; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.example.fsa.services.TranslateService; import org.json.simple.JSONObject; import java.util.Map; public class TranslateTextHandler implements RequestHandlerMapString, Object, JSONObject { Override public JSONObject handleRequest(MapString, Object requestObject, Context context) { TranslateService translateService new TranslateService(); String sourceText (String) requestObject.get(extracted_text); String lanCode (String) requestObject.get(source_language_code); context.getLogger().log(sourceText: sourceText lang code: lanCode); String translatedText translateService.translateText(lanCode, sourceText); context.getLogger().log(Translated text : translatedText); JSONObject jsonResponse new JSONObject(); jsonResponse.put(translated_text, translatedText); return jsonResponse; } }6.2 Services 包基于异步客户端的 AWS 操作包com.example.fsa.services中的类使用 AWS SDK for Java (v2)异步服务客户端执行各类 AWS 操作。从源码结构看五个类采用统一模式静态字段 private static synchronized懒加载获取单例客户端Lambda 容器可复用避免每次调用重建连接池请求发出后统一用CompletableFuture.join()阻塞等待结果——在 Lambda 这种“短任务、重结果”的场景下这种写法把异步客户端的吞吐优势与同步代码的可读性结合了起来。DetectSentimentService先定语言再定情感DetectSentimentService.java 使用ComprehendAsyncClient完成两步先用detectDominantLanguage判定文本主导语言再用该语言码调用detectSentiment判定情感。注意DetectSentimentRequest必须显式指定languageCode这正是先做语言检测的原因当无法识别语言时返回哨兵值No languages found。package com.example.fsa.services; import org.json.simple.JSONObject; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.comprehend.ComprehendAsyncClient; import software.amazon.awssdk.services.comprehend.model.ComprehendException; import software.amazon.awssdk.services.comprehend.model.DetectDominantLanguageRequest; import software.amazon.awssdk.services.comprehend.model.DetectDominantLanguageResponse; import software.amazon.awssdk.services.comprehend.model.DetectSentimentRequest; import software.amazon.awssdk.services.comprehend.model.DetectSentimentResponse; import software.amazon.awssdk.services.comprehend.model.DominantLanguage; import java.util.List; import java.util.concurrent.CompletableFuture; public class DetectSentimentService { private static ComprehendAsyncClient comprehendAsyncClient; private static synchronized ComprehendAsyncClient getComprehendAsyncClient() { if (comprehendAsyncClient null) { comprehendAsyncClient ComprehendAsyncClient.builder() .region(Region.US_EAST_1) .build(); } return comprehendAsyncClient; } public JSONObject detectSentiments(String text){ try { String languageCode detectTheDominantLanguage(text); DetectSentimentRequest detectSentimentRequest DetectSentimentRequest.builder() .text(text) .languageCode(languageCode) .build(); CompletableFuture? future getComprehendAsyncClient().detectSentiment(detectSentimentRequest); future.join(); // Wait for the operation to complete and get the result DetectSentimentResponse detectSentimentResult (DetectSentimentResponse) future.join(); JSONObject jsonObject new JSONObject(); jsonObject.put(sentiment, detectSentimentResult.sentimentAsString()); jsonObject.put(language_code, languageCode); return jsonObject; } catch (ComprehendException e) { System.err.println(e.awsErrorDetails().errorMessage()); throw e; } } public String detectTheDominantLanguage(String text){ try { DetectDominantLanguageRequest request DetectDominantLanguageRequest.builder() .text(text) .build(); CompletableFuture? future getComprehendAsyncClient().detectDominantLanguage(request); future.join(); DetectDominantLanguageResponse resp (DetectDominantLanguageResponse) future.join(); ListDominantLanguage allLanList resp.languages(); if (!allLanList.isEmpty()) { DominantLanguage firstLanguage allLanList.get(0); return firstLanguage.languageCode(); } else { return No languages found; } } catch (ComprehendException e) { System.out.println(e.getMessage()); throw e; } } }ExtractTextService只拼接 WORD 级别的 BlockExtractTextService.java 使用TextractAsyncClient对 S3 中的图片执行DetectDocumentText。响应中的文本按Block块组织代码只保留BlockType.WORD类型的块并逐个以空格拼接从而跳过 LINE/PAGE 等聚合块得到按阅读顺序排列的纯文本。异常处理上区分了业务异常TextractException与客户端异常SdkClientException两者均打印后原样重抛。package com.example.fsa.services; import software.amazon.awssdk.core.exception.SdkClientException; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.textract.model.BlockType; import software.amazon.awssdk.services.textract.model.Document; import software.amazon.awssdk.services.textract.model.DetectDocumentTextRequest; import software.amazon.awssdk.services.textract.model.DetectDocumentTextResponse; import software.amazon.awssdk.services.textract.model.Block; import software.amazon.awssdk.services.textract.TextractAsyncClient; import software.amazon.awssdk.services.textract.model.S3Object; import software.amazon.awssdk.services.textract.model.TextractException; import java.util.concurrent.CompletableFuture; public class ExtractTextService { private static TextractAsyncClient textractAsyncClient; private static synchronized TextractAsyncClient getTextractAsyncClient() { if (textractAsyncClient null) { textractAsyncClient TextractAsyncClient.builder() .region(Region.US_EAST_1) .build(); } return textractAsyncClient; } public String getCardText(String bucketName, String obName) { try { S3Object s3Object S3Object.builder() .bucket(bucketName) .name(obName) .build(); Document myDoc Document.builder() .s3Object(s3Object) .build(); DetectDocumentTextRequest detectDocumentTextRequest DetectDocumentTextRequest.builder() .document(myDoc) .build(); StringBuilder completeText new StringBuilder(); CompletableFuture? future getTextractAsyncClient().detectDocumentText(detectDocumentTextRequest); future.join(); DetectDocumentTextResponse textResponse (DetectDocumentTextResponse) future.join(); for (Block block : textResponse.blocks()) { if (block.blockType() BlockType.WORD) { if (completeText.length() 0) { completeText.append(block.text()); } else { completeText.append( ).append(block.text()); } } } return completeText.toString(); } catch (TextractException e) { System.err.println(e.awsErrorDetails().errorMessage()); throw e; // Re-throw the exception. } catch (SdkClientException e) { System.err.println(e.getMessage()); throw e; // Re-throw the exception. } } }PollyService动态发现神经语音并合成 MP3PollyService.java 的合成流程分三步构造DescribeVoicesRequest并指定engine(neural)通过PollyAsyncClient.describeVoices异步请求可用语音列表future.join()阻塞等待随后用 Java 流在结果中过滤出名为Joanna的语音找不到则抛出RuntimeException(Voice not found)构造SynthesizeSpeechRequest输入文本、OutputFormat.MP3、voiceId通过synthesizeSpeech(request, AsyncResponseTransformer.toBlockingInputStream())把响应转换为ResponseInputStream最终返回包含合成音频的InputStream。package com.example.fsa.services; import software.amazon.awssdk.core.ResponseInputStream; import software.amazon.awssdk.core.async.AsyncResponseTransformer; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.polly.PollyAsyncClient; import software.amazon.awssdk.services.polly.model.DescribeVoicesRequest; import software.amazon.awssdk.services.polly.model.PollyException; import software.amazon.awssdk.services.polly.model.SynthesizeSpeechRequest; import software.amazon.awssdk.services.polly.model.SynthesizeSpeechResponse; import software.amazon.awssdk.services.polly.model.Voice; import software.amazon.awssdk.services.polly.model.DescribeVoicesResponse; import software.amazon.awssdk.services.polly.model.OutputFormat; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.CompletableFuture; public class PollyService { private static PollyAsyncClient pollyAsyncClient; private static synchronized PollyAsyncClient getPollyAsyncClient() { if (pollyAsyncClient null) { Region region Region.US_EAST_1; pollyAsyncClient PollyAsyncClient.builder() .region(region) .build(); } return pollyAsyncClient; } public InputStream synthesize(String text) throws IOException { try { DescribeVoicesRequest describeVoicesRequest DescribeVoicesRequest.builder() .engine(neural) .build(); CompletableFuture? future getPollyAsyncClient().describeVoices(describeVoicesRequest); DescribeVoicesResponse describeVoicesResult (DescribeVoicesResponse) future.join(); Voice voice describeVoicesResult.voices().stream() .filter(v - v.name().equals(Joanna)) .findFirst() .orElseThrow(() - new RuntimeException(Voice not found)); SynthesizeSpeechRequest request SynthesizeSpeechRequest.builder() .text(text) .outputFormat(OutputFormat.MP3) .voiceId(voice.id()) .build(); CompletableFutureResponseInputStreamSynthesizeSpeechResponse audioFuture getPollyAsyncClient().synthesizeSpeech(request, AsyncResponseTransformer.toBlockingInputStream()); InputStream audioInputStream audioFuture.join(); return audioInputStream; } catch (PollyException e) { System.err.println(e.awsErrorDetails().errorMessage()); throw e; } } }S3Service用 Transfer Manager 上传 MP3S3Service.java 使用 Amazon S3 Transfer Manager API 上传 MP3 文件。实现细节值得注意底层客户端用S3AsyncClient.crtBuilder()构建即基于 AWS CRT 的高性能异步 S3 客户端对应 pom.xml 中的aws-crt依赖AsyncRequestBody.forBlockingInputStream(null)创建一个“稍后提供流”的BlockingInputStreamAsyncRequestBody——先注册上传任务再在body.writeInputStream(is)时把 Polly 返回的音频流写入Transfer Manager 负责分片与并发upload.completionFuture().join()确保上传完成后才返回。package com.example.fsa.services; import software.amazon.awssdk.core.async.AsyncRequestBody; import software.amazon.awssdk.core.async.BlockingInputStreamAsyncRequestBody; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3AsyncClient; import software.amazon.awssdk.services.s3.model.S3Exception; import software.amazon.awssdk.transfer.s3.S3TransferManager; import software.amazon.awssdk.transfer.s3.model.Upload; import java.io.IOException; import java.io.InputStream; public class S3Service { private static S3AsyncClient s3AsyncClient; private static synchronized S3AsyncClient getS3AsyncClient() { if (s3AsyncClient null) { s3AsyncClient S3AsyncClient.crtBuilder() .region(Region.US_EAST_1) .build(); } return s3AsyncClient; } // Put the audio file into the Amazon S3 bucket. public String putAudio(InputStream is, String bucketName, String key) throws S3Exception, IOException { try { S3TransferManager transferManager S3TransferManager.builder() .s3Client(getS3AsyncClient()) .build(); // null indicates a stream will be provided later. BlockingInputStreamAsyncRequestBody body AsyncRequestBody.forBlockingInputStream(null); Upload upload transferManager.upload(builder - builder .requestBody(body) .putObjectRequest(req - req.bucket(bucketName).key(key)) .build()); body.writeInputStream(is); upload.completionFuture().join(); return key; } catch (S3Exception e) { System.err.println(e.getMessage()); throw e; } } }TranslateService定向翻译为英文TranslateService.java 使用TranslateAsyncClient.translateText源语言码取自 Comprehend 的语言检测结果目标语言固定en返回textResponse.translatedText()。package com.example.fsa.services; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.translate.TranslateAsyncClient; import software.amazon.awssdk.services.translate.model.TranslateException; import software.amazon.awssdk.services.translate.model.TranslateTextRequest; import software.amazon.awssdk.services.translate.model.TranslateTextResponse; import java.util.concurrent.CompletableFuture; public class TranslateService { private static TranslateAsyncClient translateAsyncClient; private static synchronized TranslateAsyncClient getTranslateAsyncClient() { if (translateAsyncClient null) { translateAsyncClient TranslateAsyncClient.builder() .region(Region.US_EAST_1) .build(); } return translateAsyncClient; } public String translateText(String lanCode, String text) { try { TranslateTextRequest textRequest TranslateTextRequest.builder() .sourceLanguageCode(lanCode) .targetLanguageCode(en) .text(text) .build(); CompletableFuture? future getTranslateAsyncClient().translateText(textRequest); TranslateTextResponse textResponse (TranslateTextResponse) future.join(); return textResponse.translatedText(); } catch (TranslateException e) { System.err.println(e.awsErrorDetails().errorMessage()); throw e; } } }七、部署 AWS 资源至此业务逻辑代码全部完成接下来部署 Lambda 函数与 API Gateway 端点等 AWS 资源。不要逐个在控制台中手动创建直接运行配套提供的 AWS CDK 脚本即可。完整的 CDK 运行说明见 Feedback Sentiment Analyzer (FSA)。7.1 检查 Lambda 角色的权限每个 Lambda 函数都有一个用于调用该函数的角色例如AnalyzeSentiment函数的角色形如java-AnalyzeSentimentServiceR-14R5C2CE32WCM具体名称取决于 CDK 配置。请确保每个角色具备调用对应服务的权限否则可能出现如下报错user: arn:aws:sts::xxxx:assumed-role/fsa-user-java-fnTranslateTextServiceRole41F86E1F-7K31GKO5HOUF is not authorized to perform: comprehend:DetectDominantLanguage because no identity-based policy allows the comprehend:DetectDominantLanguage action该报错直接对应DetectSentimentService.detectTheDominantLanguage的调用链fnTranslateText所关联的角色缺少comprehend:DetectDominantLanguage身份策略。修复方式是按 IAM 文档为角色补充相应服务权限。7.2 检查 Lambda 配置选项可检查并调整各函数的超时时间timeout与内存分配等配置项例如 Textract OCR 类任务通常需要比简单文本处理更长的超时预算。7.3 运行应用运行完 AWS CDK 脚本后使用 CDK 说明中给出的 Amazon CloudFront 分发 URL 访问客户端应用。在 React 客户端上传图片后工作流被触发可在 AWS Step Functions 中查看执行进度成功完成时每个状态均为绿色S3 存储桶中出现对应的.mp3文件DynamoDB 表中出现一条新记录。八、本地验证集成测试用例仓库在 FSATests.java 中提供了五个按Order排序的Tag(IntegrationTest)集成测试无需部署 Lambda 就能在本地串起整条服务链testTextExtractiontextService.getCardText(bucket, object)断言非空testSentimentAnalysis提取文本后做情感检测testTranslation先用detectTheDominantLanguage取得语言码再翻译testAudioSynthesis走完“提取 → 语言 → 翻译 → Polly 合成”全链路testS3Upload最后一步把objectName .mp3通过s3Service.putAudio上传到存储桶。运行前需把文件顶部的bucketName与objectName两个常量替换为你自己的存储桶与图片对象名并确保本地凭证具备 S3、Textract、Comprehend、Translate、Polly 权限。这五个测试方法也恰好复刻了 Step Functions 工作流中 ExtractText → AnalyzeSentiment → TranslateText → SynthesizeAudio → PutPositiveComment 的数据流可以视作工作流的本地等价验证。九、收尾与后续步骤部署验证完成后务必按照 CDK 说明删除全部资源避免持续计费所有客户端类中Region.US_EAST_1为硬编码若在其他区域部署需同步修改五个 Service 类中的区域设置更多 AWS 多服务示例可参考 javav2/usecases 目录下的其他用例。相关文件索引教程文档READMEMaven 配置pom.xmlLambda 处理程序handlers 包异步服务封装services 包集成测试FSATests.java配套 CDK 部署说明feedback_sentiment_analyzer README赞分享示例工程教程后端【免费下载链接】aws-doc-sdk-examplesWelcome 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 Java SDK构建反馈情感分析应用的技术实践使用AWS Java SDK构建反馈情感分析应用的技术实践 引言客户反馈处理的智能化革命 在当今数字化时代企业每天都会收到大量的客户反馈这些反馈可能来自各示例工程教程后端Feedback Sentiment AnalyzerFSA实战指南基于 AWS 多语言 SDK 构建跨服务评论情感分析应用Feedback Sentiment AnalyzerFSA实战指南基于 AWS 多语言 SDK 构建跨服务评论情感分析应用 Feedback Senti示例工程教程后端RepViT架构解密repvit_m0_9.dist_300e_in1k如何融合CNN与ViT优势RepViT架构解密repvit_m0_9.dist_300e_in1k如何融合CNN与ViT优势 repvit_m0_9.dist_300e_in1k是一款上一篇PowerDNS三大组件对比何时选择权威、递归还是负载均衡下一篇Czkawka Windows 安装指南3 步装好重复文件清理神器创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考