aws(学习笔记第四十一课) image-content-search

文章目录

  • aws(学习笔记第四十一课) image-content-search
  • 学习内容:
    • 1. 整体架构
      • 1.1 代码链接
      • 1.2 关键架构流程
      • 1.3 `upload`图像文件的动作
      • 1.4 `search`图像文件的动作
    • 2. 代码解析
      • 2.1 `yml`文件配置详细设定
        • 2.1.1 `yml`文件
        • 2.1.2 `yml`文件文件解析
      • 2.2 创建`s3 bucket`
      • 2.3 创建`API Gateway`
      • 2.4 创建文件上传的表示页面
        • 2.4.1 创建文件上传的`api_gateway_resource`的`lambda`函数
        • 2.4.2 配置文件上传的`LambdaIntegration`
        • 2.4.3 配置文件上传的`API Gateway Method`
        • 2.4.4 配置文件上传的页面代码
        • 2.4.4.1 `python`的`lambda handler`
        • 2.4.4.2 `html`的页面代码
      • 2.5 配置`cognito`安全认证
      • 2.6 创建`signedURL`的`API Gateway`
        • 2.6.1 创建`signedURL`的`API Gateway`
        • 2.6.2 创建`signedURL`的`API Gateway`处理的`lambda`
      • 2.7 监视`S3 bucket`的`lambda`
        • 2.7.1 监视架构
        • 2.7.2 创建`lmabda`
        • 2.7.2 `S3 bucket`监视的`lmabda`代码
      • 2.8 创建`lambda`对图像进行分析
        • 2.8.1 图像分析架构
        • 2.8.1 图像分析`lambda`函数
      • 2.9 创建图像分析数据保存的数据库
        • 2.9.1 创建数据库的密码
        • 2.9.2 创建数据库
        • 2.9.3 将数据库密码和数据库绑定`attachment`
      • 2.10 创建数据库的`lambda function`
        • 2.10.1 创建数据库访问`role`
        • 2.10.2 创建`image_data_function`
        • 2.10.3 创建`image search function`
        • 2.10.4 创建`image db`的`schema`
        • 2.10.5 创建`image db`的保存`lambda`
    • 3 执行`cdk`

aws(学习笔记第四十一课) image-content-search

  • 使用SQS + Lambda集成
  • 数据库(Aurora Serverless
  • Cognito(用户管理)
  • rekognition(图像解析)

学习内容:

  • 使用SQS + Lambda+ Aurora Serverless + Cognito + rekognition

1. 整体架构

1.1 代码链接

  • 代码链接(image-content-search)

1.2 关键架构流程

  • 用户上传图像 → S3触发Lambda → 图像分析 → 结果存入数据库
  • 前端通过API Gateway查询数据库(需Cognito认证)
  • 事件总线协调异步任务(如分析完成后触发存储操作)。

1.3 upload图像文件的动作

在这里插入图片描述

1.4 search图像文件的动作

在这里插入图片描述

2. 代码解析

2.1 yml文件配置详细设定

2.1.1 yml文件

这里配置了EnvironmentAuthorRegion等配置,比起写入cdk.pypython代码中,这里将配置数据写入yml文件中会更加清晰。

Environment: Development
Author: Mohsen
Region: eu-central-1
ProjectName: ImageContentSearchDeadLetterQueue:MaxReceiveCount: 3Cognito:SelfSignUp: TrueDomainPrefix: image-content-searchAllowedOAuthScopes:- phone- email- openid- profileDatabase:Name: images_labelsDeletionProtection: FalseScaling:AutoPause: TrueMin: 2Max: 8SecondsToAutoPause: 1800Functions:DefaultSignedUrlExpirySeconds: "3600"DefaultMaxApiCallAttempts: "5"
2.1.2 yml文件文件解析
 with open("stack/config.yml", 'r') as stream:configs = yaml.safe_load(stream)# for example, use configs in image_data_functionimage_data_function = Function(self, "ICS_IMAGE_DATA",function_name="ICS_IMAGE_DATA",runtime=Runtime.PYTHON_3_7,timeout=Duration.seconds(5),role=image_data_function_role,environment={"DEFAULT_MAX_CALL_ATTEMPTS": configs["Functions"]["DefaultMaxApiCallAttempts"],"CLUSTER_ARN": database_cluster_arn,"CREDENTIALS_ARN": database_secret.secret_arn,"DB_NAME": database.database_name,"REGION": Aws.REGION},handler="main.handler",code=Code.from_asset("./src/imageData"))

2.2 创建s3 bucket

        ### S3 coreimages_S3_bucket = _s3.Bucket(self, "ICS_IMAGES")images_S3_bucket.add_cors_rule(allowed_methods=[_s3.HttpMethods.POST],allowed_origins=["*"] # add API gateway web resource URL)

这里,需要从API gatewaydomain进行跨域访问S3 bucketAWSurl,因此需要CORS Cross-Origin Resource Share,是浏览器的一种安全机制,用于控制不同源(协议+域名+端口)之间的资源访问。
在之前的文章中介绍过。spring boot(学习笔记第五课) 自定义错误页,CORS(跨域支持)

2.3 创建API Gateway

 ### api gateway coreapi_gateway = RestApi(self, 'ICS_API_GATEWAY', rest_api_name='ImageContentSearchApiGateway')api_gateway_resource = api_gateway.root.add_resource(configs["ProjectName"])api_gateway_landing_page_resource = api_gateway_resource.add_resource('web')api_gateway_get_signedurl_resource = api_gateway_resource.add_resource('signedUrl')api_gateway_image_search_resource = api_gateway_resource.add_resource('search')
  • api_gateway_resource作为父resouce
  • api_gateway_landing_page_resource作为子resource,作为文件上传的表示页面。
  • api_gateway_landing_page_resource作为子resource,作为文件上传S3 bucket的请求url
  • api_gateway_image_search_resource作为子resource,作为文件分析结果的页面。

2.4 创建文件上传的表示页面

2.4.1 创建文件上传的api_gateway_resourcelambda函数
 ### landing page functionget_landing_page_function = Function(self, "ICS_GET_LANDING_PAGE",function_name="ICS_GET_LANDING_PAGE",runtime=Runtime.PYTHON_3_7,handler="main.handler",code=Code.from_asset("./src/landingPage"))
2.4.2 配置文件上传的LambdaIntegration
get_landing_page_integration = LambdaIntegration(get_landing_page_function,proxy=True,integration_responses=[IntegrationResponse(status_code='200',response_parameters={'method.response.header.Access-Control-Allow-Origin': "'*'"})])

注意,这里配置method.response.header.Access-Control-Allow-Origin以便允许其他domain过来的跨域的访问。但是,如果是生产环境,需要将*换成特定的domain

2.4.3 配置文件上传的API Gateway Method
api_gateway_landing_page_resource.add_method('GET', get_landing_page_integration,method_responses=[MethodResponse(status_code='200',response_parameters={'method.response.header.Access-Control-Allow-Origin': True})])
2.4.4 配置文件上传的页面代码

在这里插入图片描述

2.4.4.1 pythonlambda handler

\src\landingPage\main.py

# this function
# gets the simple html page
# updates the login page and logout page address
# returns the contentdef handler(event, context):login_page = event["headers"]["Referer"]return {'statusCode': 200,'headers': {'Content-Type': 'text/html'},'body': file_get_contents("index.html").replace('###loginPage###', login_page)}def file_get_contents(filename):with open(filename) as f:return f.read()

这里,直接将html的文件打开,进行返回

2.4.4.2 html的页面代码

\src\landingPage\index.html

<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1"><style>body{font-family:Arial}.tab{overflow:hidden;border:1px solid #ccc;background-color:#f1f1f1}.tab button{background-color:inherit;float:left;border:none;outline:0;cursor:pointer;padding:14px 16px;transition:.3s;font-size:17px}.tab button:hover{background-color:#ddd}.tab button.active{background-color:#ccc}.tabcontent{display:none;padding:6px 12px;-webkit-animation:fadeEffect 1s;animation:fadeEffect 1s}@-webkit-keyframes fadeEffect{from{opacity:0}to{opacity:1}}@keyframes fadeEffect{from{opacity:0}to{opacity:1}}input[type=text],select{width:30%;padding:12px 20px;margin:8px 0;display:inline-block;border:1px solid #ccc;border-radius:4px;box-sizing:border-box}.submit[type=submit]{width:20%;background-color:#4caf50;color:#fff;padding:14px 20px;margin:8px 0;border:none;border-radius:4px;cursor:pointer}input[type=submit]:hover{background-color:#45a049}.div{border-radius:5px;background-color:#f2f2f2;padding:20px}table{border-collapse:collapse;table-layout: fixed;width:100%}td,th{text-align:left;padding:8px;word-wrap:break-word;}tr:nth-child(even){background-color:#f2f2f2}</style><script src="https://code.jquery.com/jquery-3.6.4.min.js"></script><script>var authData = window.location.hash.substring(1);var idToken = authData.split('&').find((rec) => rec.split('=')[0] == 'id_token').split('=')[1]var getSignedUrlEndpoint = window.location.href.split('#')[0].replace('web', 'signedUrl')var searchImageEndpoint = window.location.href.split('#')[0].replace('web', 'search')var loginPage = '###loginPage###';var logoutPage = loginPage.replace('login', 'logout');function logout() {window.location.replace(logoutPage);}function getSignedUrl() {$.ajax({url: getSignedUrlEndpoint,headers: { 'Authorization': idToken },type: "GET",contentType: 'application/json; charset=utf-8',success: function (result) {console.log(result);$("#upload_image_form").attr('action', result.url);$('input[name="key"]').val(result.fields.key);$('input[name="X-Amz-Credential"]').val(result.fields['x-amz-credential']);$('input[name="X-Amz-Algorithm"]').val(result.fields['x-amz-algorithm']);$('input[name="X-Amz-Date"]').val(result.fields['x-amz-date']);$('input[name="x-amz-security-token"]').val(result.fields['x-amz-security-token']);$('input[name="Policy"]').val(result.fields.policy);$('input[name="X-Amz-Signature"]').val(result.fields['x-amz-signature']);},error: function (error) {console.log(error);if (error.status == 401) {logout();}}});}function listImagesByLabel(outputTab, label, language, country) {console.log('Finding images with label: ' + label);var formData = language ? {label, language, country} : {label}$.ajax({url: searchImageEndpoint,headers: { 'Authorization': idToken },type: "POST",data: {...formData, 'source': 'API'},contentType: 'application/json; charset=utf-8',success: function (results) {console.log(results);$(outputTab + " tr").remove();$(outputTab + " th").remove();if (results) {$(outputTab).append( '<tr><th>Image ID</th></tr>' );results.forEach(item => {$(outputTab).append( '<tr><td>' + item.id + '</th></tr>' );});}},error: function (error) {console.log(error.responseText, error.status);if (error.status == 401) {logout();}}});}$(document).ready(function () {if (window.location.hash) {// getSignedUrl();} else {console.log('Authorization information from cognito is not found!');}});function submitSearchQuery() {event.preventDefault();var language = $('#language').val();var country = $('#country').val();var label = $('input[name=label]').val();listImagesByLabel('#search_image_result', label, language, country);}function openTab(evt, tabName) {$("#upload_result").text('');$("#upload_result").css("color", "black");$("#file_select").val('');if (tabName == 'upload') {getSignedUrl();}if (tabName == 'report') {listImagesByLabel('#report_image_result', 'offensive');}var i, tabcontent, tablinks;tabcontent = document.getElementsByClassName("tabcontent");for (i = 0; i < tabcontent.length; i++) {tabcontent[i].style.display = "none";}tablinks = document.getElementsByClassName("tablinks");for (i = 0; i < tablinks.length; i++) {tablinks[i].className = tablinks[i].className.replace(" active", "");}document.getElementById(tabName).style.display = "block";evt.currentTarget.className += " active";}function submitFileUpload() {event.preventDefault();var formData = new FormData();var selectedFile = $('input[name="file"]')[0]$('#upload_image_form *').filter(':input').filter(":hidden").each(function(k, v){formData.append(v.name, v.defaultValue);});formData.append("file", selectedFile.files[0]);$.ajax({url: $("#upload_image_form").attr('action'),type: 'POST',data: formData,success: function (data) {$("#upload_result").text('The file has been successfully uploaded!');$("#upload_result").css("color", "green");getSignedUrl();},error: function(xhr, textStatus, errorThrown){$("#upload_result").text('The file upload failed!');$("#upload_result").css("color", "red");console.log(textStatus);console.log(errorThrown);},cache: false,contentType: false,processData: false});};</script>
</head><body><div style="width: 50%; margin-left: 25%;"><div class="tab" style="margin-top: 10px;"><button class="tablinks" onclick="openTab(event, 'upload')" id="default_tab">Upload</button><button class="tablinks" onclick="openTab(event, 'search')">Search</button><button class="tablinks" onclick="openTab(event, 'report')">Report</button><button class="tablinks" onclick="logout()" style="float: right;">Logout</button></div><div id="upload" class="tabcontent"><h3>Upload Image</h3><p>Select image to upload:</p><form id="upload_image_form" method="post" enctype="multipart/form-data"><input type="hidden" name="key"/><br /><input type="hidden" name="X-Amz-Credential"/><input type="hidden" name="X-Amz-Algorithm"/><input type="hidden" name="X-Amz-Date"/><input type="hidden" name="x-amz-security-token"/><input type="hidden" name="Policy"/><input type="hidden" name="X-Amz-Signature"/><input type="file" id="file_select" name="file"/> <br /><input type="submit" class="submit" value="Upload" onclick="submitFileUpload()"/></form><p id="upload_result"></p></div><div id="search" class="tabcontent"><h3>Search Labels</h3><form id="search_image_form" method="post"><label >Language:</label><select name="language" id="language"><option value="en">English</option><option value="tr">Turkish</option><option value="nl">Dutch</option></select><br /><label >Country:</label><select name="country" id="country"><option value="nl">Netherlands</option></select><br /><label >Label to search:</label><input type="text" name="label"/><br /><input class="submit" type="submit" value="Search" onclick="submitSearchQuery()"/></form><table id="search_image_result"></table></div><div id="report" class="tabcontent"><h3>Report of offensive photos</h3><table id="report_image_result"></table></div></div><script>document.getElementById("default_tab").click();</script></body></html>

2.5 配置cognito安全认证

 ### cognitorequired_attribute = _cognito.StandardAttribute(required=True)users_pool = _cognito.UserPool(self, "ICS_USERS_POOL",auto_verify=_cognito.AutoVerifiedAttrs(email=True), #required for self sign-upstandard_attributes=_cognito.StandardAttributes(email=required_attribute), #required for self sign-upself_sign_up_enabled=configs["Cognito"]["SelfSignUp"])user_pool_app_client = _cognito.CfnUserPoolClient(self, "ICS_USERS_POOL_APP_CLIENT",supported_identity_providers=["COGNITO"],allowed_o_auth_flows=["implicit"],allowed_o_auth_scopes=configs["Cognito"]["AllowedOAuthScopes"],user_pool_id=users_pool.user_pool_id,callback_urls=[api_gateway.url_for_path('/web')],allowed_o_auth_flows_user_pool_client=True,explicit_auth_flows=["ALLOW_REFRESH_TOKEN_AUTH"])

这里表示/web需要认证,并且认证之后url将重定向到/web

2.6 创建signedURLAPI Gateway

2.6.1 创建signedURLAPI Gateway
        ### get signed URL functionget_signedurl_function = Function(self, "ICS_GET_SIGNED_URL",function_name="ICS_GET_SIGNED_URL",environment={"ICS_IMAGES_BUCKET": images_S3_bucket.bucket_name,"DEFAULT_SIGNEDURL_EXPIRY_SECONDS": configs["Functions"]["DefaultSignedUrlExpirySeconds"]},runtime=Runtime.PYTHON_3_7,handler="main.handler",code=Code.from_asset("./src/getSignedUrl"))get_signedurl_integration = LambdaIntegration(get_signedurl_function,proxy=True,integration_responses=[IntegrationResponse(status_code='200',response_parameters={'method.response.header.Access-Control-Allow-Origin': "'*'",})])api_gateway_get_signedurl_authorizer = CfnAuthorizer(self, "ICS_API_GATEWAY_GET_SIGNED_URL_AUTHORIZER",rest_api_id=api_gateway_get_signedurl_resource.api.rest_api_id,name="ICS_API_GATEWAY_GET_SIGNED_URL_AUTHORIZER",type="COGNITO_USER_POOLS",identity_source="method.request.header.Authorization",provider_arns=[users_pool.user_pool_arn])get_signedurl_method = api_gateway_get_signedurl_resource.add_method('GET', get_signedurl_integration,authorization_type=AuthorizationType.COGNITO,method_responses=[MethodResponse(status_code='200',response_parameters={'method.response.header.Access-Control-Allow-Origin': True,})])signedurl_custom_resource = typing.cast("aws_cloudformation.CfnCustomResource", get_signedurl_method.node.find_child('Resource'))signedurl_custom_resource.add_property_override('AuthorizerId', api_gateway_get_signedurl_authorizer.ref)images_S3_bucket.grant_put(get_signedurl_function, objects_key_pattern="new/*")
2.6.2 创建signedURLAPI Gateway处理的lambda

这里,从前端传递来的文件被putS3 bucket

import json
import boto3
import logging
import os
import time
import hashlibfrom botocore.exceptions import ClientError
images_bucket = os.environ['ICS_IMAGES_BUCKET']
default_signedurl_expiry_seconds = os.environ['DEFAULT_SIGNEDURL_EXPIRY_SECONDS']# this function
# creates a pre-sighned URL for uploading image to S3 and returns itdef handler(event, context):uniquehash = hashlib.sha1("{}".format(time.time_ns()).encode('utf-8')).hexdigest()result = create_presigned_post(images_bucket, "new/{}/{}".format(uniquehash[:2],uniquehash))return {'statusCode': 200,'headers': {'Content-Type': 'application/json; charset=UTF-8'},'body': json.dumps(result)}def create_presigned_post(bucket_name, object_name, fields=None, conditions=None, expiration=default_signedurl_expiry_seconds):s3_client = boto3.client('s3')try:response = s3_client.generate_presigned_post(bucket_name,object_name,Fields=fields,Conditions=conditions,ExpiresIn=int(expiration))except ClientError as e:logging.error(e)return Nonereturn response

2.7 监视S3 bucketlambda

2.7.1 监视架构

在这里插入图片描述

2.7.2 创建lmabda
### image massage functionimage_massage_function = Function(self, "ICS_IMAGE_MASSAGE",function_name="ICS_IMAGE_MASSAGE",timeout=Duration.seconds(6),runtime=Runtime.PYTHON_3_7,environment={"ICS_IMAGE_MASSAGE": image_queue.queue_name},handler="main.handler",code=Code.from_asset("./src/imageMassage"))images_S3_bucket.grant_write(image_massage_function, "processed/*")images_S3_bucket.grant_delete(image_massage_function, "new/*")images_S3_bucket.grant_read(image_massage_function, "new/*")new_image_added_notification = _s3notification.LambdaDestination(image_massage_function)images_S3_bucket.add_event_notification(_s3.EventType.OBJECT_CREATED,new_image_added_notification,_s3.NotificationKeyFilter(prefix="new/"))image_queue.grant_send_messages(image_massage_function)
2.7.2 S3 bucket监视的lmabda代码
def handler(event, context):s3 = boto3.resource('s3')for record in event['Records']:newKey = record['s3']['object']['key']bucket = record['s3']['bucket']['name']name = bucket.split("/")[-1]localfile = "/tmp/{}".format(name)# download the filenew_key_obj = s3.Object(bucket, newKey)new_key_obj.download_file(localfile)# calc hashimage_SHA1 = getSha1(localfile)# check if not existprocessed_key = "processed/{}/{}".format(image_SHA1[:2], image_SHA1)key_is_processed = isS3ObjectExist(bucket, processed_key)if key_is_processed: continue# add to the queuemessage = json.dumps({"image": processed_key,"original_key": newKey,"original_last_modified": new_key_obj.last_modified,"etag": new_key_obj.e_tag}, default=str)queue = sqs.get_queue_by_name(QueueName=queue_name)response = queue.send_message(MessageBody=message)logger.info("Message {} has been sent.".format(response.get('MessageId')))#move the images3.Object(bucket, processed_key).copy_from(CopySource="{}/{}".format(bucket,newKey))new_key_obj.delete()# delete local fileos.remove(localfile)return Truedef isS3ObjectExist(bucket, key):s3 = boto3.resource('s3')try:s3.Object(bucket,key)return Falseexcept botocore.exceptions.ClientError as e:if e.response['Error']['Code'] == "404":return Trueelse:raise edef getSha1(filepath):sha1 = hashlib.sha1()with open(filepath, 'rb') as f:while True:data = f.read(65536) # read in 64kb chunksif not data: breaksha1.update(data)return sha1.hexdigest()

2.8 创建lambda对图像进行分析

2.8.1 图像分析架构

在这里插入图片描述

2.8.1 图像分析lambda函数
def handler(event, context):for record in event['Records']:# receiptHandle = record['receiptHandle']body = record['body']message = json.loads(body)bucket = os.environ['ICS_IMAGES_BUCKET']key = message['image']# original_key = message['original_key']# original_last_modified = message['original_last_modified']# etag = message['etag']logger.info('Processing {}.'.format(key))detected_labels = rekognition_client.detect_labels(Image={'S3Object': {'Bucket': bucket, 'Name': key}},MaxLabels=20,MinConfidence=85)detected_unsafe_contents = rekognition_client.detect_moderation_labels(Image={'S3Object': {'Bucket': bucket, 'Name': key}})object_labels = []for l in detected_labels['Labels']:object_labels.append(l['Name'].lower()) # add objects in imagefor l in detected_unsafe_contents['ModerationLabels']:if ('offensive' not in object_labels): object_labels.append("offensive") #label image as offensiveobject_labels.append(l['Name'].lower())image_id = key.split("/")[-1]response = events_client.put_events(Entries=[{'Source': "EventBridge",'Resources': [context.invoked_function_arn,],'DetailType': 'images_labels','Detail': json.dumps({"labels": object_labels, "image_id": image_id}),'EventBusName': event_bus_name},])if response["FailedEntryCount"] == 1:raise Exception(f'Failed entry observed. Count: {response["Entries"]}')

2.9 创建图像分析数据保存的数据库

2.9.1 创建数据库的密码
### databasedatabase_secret = _secrets_manager.Secret(self, "ICS_DATABASE_SECRET",secret_name="rds-db-credentials/image-content-search-rds-secret",generate_secret_string=_secrets_manager.SecretStringGenerator(generate_string_key='password',secret_string_template='{"username": "dba"}',exclude_punctuation=True,exclude_characters='/@\" \\\'',require_each_included_type=True))
2.9.2 创建数据库
database = _rds.CfnDBCluster(self, "ICS_DATABASE",engine=_rds.DatabaseClusterEngine.aurora_mysql(version=_rds.AuroraMysqlEngineVersion.VER_5_7_12).engine_type,engine_mode="serverless",database_name=configs["Database"]["Name"],enable_http_endpoint=True,deletion_protection=configs["Database"]["DeletionProtection"],master_username=database_secret.secret_value_from_json("username").to_string(),master_user_password=database_secret.secret_value_from_json("password").to_string(),scaling_configuration=_rds.CfnDBCluster.ScalingConfigurationProperty(auto_pause=configs["Database"]["Scaling"]["AutoPause"],min_capacity=configs["Database"]["Scaling"]["Min"],max_capacity=configs["Database"]["Scaling"]["Max"],seconds_until_auto_pause=configs["Database"]["Scaling"]["SecondsToAutoPause"]),)
2.9.3 将数据库密码和数据库绑定attachment
        database_cluster_arn = "arn:aws:rds:{}:{}:cluster:{}".format(Aws.REGION, Aws.ACCOUNT_ID, database.ref)secret_target = _secrets_manager.CfnSecretTargetAttachment(self,"ICS_DATABASE_SECRET_TARGET",target_type="AWS::RDS::DBCluster",target_id=database.ref,secret_id=database_secret.secret_arn)secret_target.node.add_dependency(database)

2.10 创建数据库的lambda function

2.10.1 创建数据库访问role
        ### database functionimage_data_function_role = _iam.Role(self, "ICS_IMAGE_DATA_FUNCTION_ROLE",role_name="ICS_IMAGE_DATA_FUNCTION_ROLE",assumed_by=_iam.ServicePrincipal("lambda.amazonaws.com"),managed_policies=[_iam.ManagedPolicy.from_aws_managed_policy_name("service-role/AWSLambdaVPCAccessExecutionRole"),_iam.ManagedPolicy.from_aws_managed_policy_name("service-role/AWSLambdaBasicExecutionRole"),_iam.ManagedPolicy.from_aws_managed_policy_name("AmazonRDSDataFullAccess")])
2.10.2 创建image_data_function

在这里插入图片描述

       image_data_function = Function(self, "ICS_IMAGE_DATA",function_name="ICS_IMAGE_DATA",runtime=Runtime.PYTHON_3_7,timeout=Duration.seconds(5),role=image_data_function_role,environment={"DEFAULT_MAX_CALL_ATTEMPTS": configs["Functions"]["DefaultMaxApiCallAttempts"],"CLUSTER_ARN": database_cluster_arn,"CREDENTIALS_ARN": database_secret.secret_arn,"DB_NAME": database.database_name,"REGION": Aws.REGION},handler="main.handler",code=Code.from_asset("./src/imageData"))
2.10.3 创建image search function

在这里插入图片描述

        image_search_integration = LambdaIntegration(image_data_function,proxy=True,integration_responses=[IntegrationResponse(status_code='200',response_parameters={'method.response.header.Access-Control-Allow-Origin': "'*'",})])api_gateway_image_search_authorizer = CfnAuthorizer(self, "ICS_API_GATEWAY_IMAGE_SEARCH_AUTHORIZER",rest_api_id=api_gateway_image_search_resource.api.rest_api_id,name="ICS_API_GATEWAY_IMAGE_SEARCH_AUTHORIZER",type="COGNITO_USER_POOLS",identity_source="method.request.header.Authorization",provider_arns=[users_pool.user_pool_arn])search_integration_method = api_gateway_image_search_resource.add_method('POST', image_search_integration,authorization_type=AuthorizationType.COGNITO,method_responses=[MethodResponse(status_code='200',response_parameters={'method.response.header.Access-Control-Allow-Origin': True,})])search_integration_custom_resource = typing.cast("aws_cloudformation.CfnCustomResource", search_integration_method.node.find_child('Resource'))search_integration_custom_resource.add_property_override('AuthorizerId', api_gateway_image_search_authorizer.ref)

在这里插入图片描述

2.10.4 创建image dbschema
        ### custom resourcelambda_provider = Provider(self, 'ICS_IMAGE_DATA_PROVIDER',on_event_handler=image_data_function)CustomResource(self, 'ICS_IMAGE_DATA_RESOURCE',service_token=lambda_provider.service_token,pascal_case_properties=False,resource_type="Custom::SchemaCreation",properties={"source": "Cloudformation"})

在这里插入图片描述

2.10.5 创建image db的保存lambda

在这里插入图片描述
image_analyzer_function保存分析结果到event_bus之中,这里继续将event_rule.add_target(_event_targets.LambdaFunction(image_data_function)),之后image_data_function会将数据保存到数据库。

        ### event bridgeevent_bus = _events.EventBus(self, "ICS_IMAGE_CONTENT_BUS", event_bus_name="ImageContentBus")event_rule = _events.Rule(self, "ICS_IMAGE_CONTENT_RULE",rule_name="ICS_IMAGE_CONTENT_RULE",description="The event from image analyzer to store the data",event_bus=event_bus,event_pattern=_events.EventPattern(resources=[image_analyzer_function.function_arn]),)event_rule.add_target(_event_targets.LambdaFunction(image_data_function))

在这里插入图片描述

3 执行cdk

TODO

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/78764.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Python+MongoDB猫眼电影 Top100 数据爬取与存储

前言&#xff1a;从猫眼电影排行榜页面&#xff08;TOP100榜 - 猫眼电影 - 一网打尽好电影 &#xff09;爬取 Top100 电影的电影名称、图片地址、主演、上映时间和评分等关键信息&#xff0c;并将这些信息存储到本地 MongoDB 数据库中&#xff0c;&#x1f517; 相关链接Xpath&…

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】2.5 事务与锁机制(ACID特性/事务控制语句)

👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 文章大纲 PostgreSQL 事务与锁机制深度解析:ACID 特性与事务控制全流程2.5 事务与锁机制2.5.1 ACID 特性与实现原理2.5.1.1 ACID 核心概念2.5.1.2 MVCC(多版本并发控制)与WAL(预写式日志)协同效应2.5.2 事务…

荣耀A8互动娱乐组件部署实录(终章:后台配置系统与整体架构总结)

作者:被配置文件的“开关参数”折磨过无数次的运维兼后端工 一、后台系统架构概述 荣耀A8组件后台采用 PHP 构建,配合 MySQL 数据库与 Redis 缓存系统,整体结构遵循简化版的 MVC 模式。后台主要实现以下核心功能: 系统参数调控与配置热更新 用户管理(封号、授权、角色) …

Transformer 与 LSTM 在时序回归中的实践与优化

&#x1f9e0; 深度学习混合模型&#xff1a;Transformer 与 LSTM 在时序回归中的实践与优化 在处理多特征输入、多目标输出的时序回归任务时&#xff0c;结合 Transformer 和 LSTM 的混合模型已成为一种有效的解决方案。Transformer 擅长捕捉长距离依赖关系&#xff0c;而 LS…

QT —— 信号和槽(带参数的信号和槽函数)

QT —— 信号和槽&#xff08;带参数的信号和槽函数&#xff09; 带参的信号和槽函数信号参数个数和槽函数参数个数1. 参数匹配规则2. 实际代码示例✅ 合法连接&#xff08;槽参数 ≤ 信号参数&#xff09;❌ 非法连接&#xff08;槽参数 > 信号参数&#xff09; 3. 特殊处理…

设计模式简述(十七)备忘录模式

备忘录模式 描述组件使用 描述 备忘录模式用于将对象的状态进行保存为备忘录&#xff0c;以便在需要时可以从备忘录会对象状态&#xff1b;其核心点在于备忘录对象及其管理者是独立于原有对象之外的。 常用于需要回退、撤销功能的场景。 组件 原有对象&#xff08;包含自身…

标签语句分析

return userList.stream().filter(user -> {String tagsStr user.getTags(); 使用 Stream API 来过滤 userList 中的用户 解析 tagsStr 并根据标签进行过滤 假设 tagsStr 是一个 JSON 格式的字符串&#xff0c;存储了一个标签集合。你希望过滤出包含所有指定标签的用户。…

【应用密码学】实验四 公钥密码1——数学基础

一、实验要求与目的 学习快速模幂运算、扩展欧几里得、中国剩余定理的算法思想以及代码实现。 二、实验内容与步骤记录&#xff08;只记录关键步骤与结果&#xff0c;可截图&#xff0c;但注意排版与图片大小&#xff09; 1.快速模幂运算的设计思路 快速模幂运算的核心思想…

WebSocket与Socket、TCP、HTTP的关系及区别

1.什么是WebSocket及原理 WebSocket是HTML5中新协议、新API。 WebSocket从满足基于Web的日益增长的实时通信需求应运而生&#xff0c;解决了客户端发起多个Http请求到服务器资源浏览器必须要在经过长时间的轮询问题&#xff0c;实现里多路复用&#xff0c;是全双工、双向、单套…

基于C++的IOT网关和平台4:github项目ctGateway交互协议

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。 源码指引:github源码指引_初级代码游戏的博客-CSDN博客 系…

【PPT制作利器】DeepSeek + Kimi生成一个初始的PPT文件

如何基于DeepSeek Kimi进行PPT制作 步骤&#xff1a; Step1&#xff1a;基于DeepSeek生成文本&#xff0c;提问 Step2基于生成的文本&#xff0c;用Kimi中PPT助手一键生成PPT 进行PPT渲染-自动渲染 可选择更改模版 生成PPT在桌面 介绍的比较详细&#xff0c;就是这个PPT模版…

拷贝多个Excel单元格区域为图片并粘贴到Word

Excel工作表Sheet1中有两个报表&#xff0c;相应单元格区域分别定义名称为Report1和Report2&#xff0c;如下图所示。 现在需要将图片拷贝图片粘贴到新建的Word文档中。 示例代码如下。 Sub Demo()Dim oWordApp As ObjectDim ws As Worksheet: Set ws ThisWorkbook.Sheets(&…

Spring是如何传播事务的?什么是事务传播行为

Spring是如何传播事务的&#xff1f; Spring框架通过声明式事务管理来传播事务&#xff0c;主要依赖于AOP&#xff08;面向切面编程&#xff09;和事务拦截器来实现。Spring的事务传播机制是基于Java Transaction API (JTA) 或者本地资源管理器&#xff08;如Hibernate、JDBC等…

Python-pandas-操作Excel文件(读取数据/写入数据)及Excel表格列名操作详细分享

Python-pandas-操作Excel文件(读取数据/写入数据) 提示&#xff1a;帮帮志会陆续更新非常多的IT技术知识&#xff0c;希望分享的内容对您有用。本章分享的是pandas的使用语法。前后每一小节的内容是存在的有&#xff1a;学习and理解的关联性。【帮帮志系列文章】&#xff1a;每…

PHP分页显示数据,在phpMyadmin中添加数据

<?php $conmysqli_connect(localhost,root,,stu); mysqli_query($con,"set names utf8"); //设置字符集为utf8 $sql"select * from teacher"; $resultmysqli_query($con,$sql); $countmysqli_num_rows($result); //记录总条数$count。 $pagesize10;//每…

智能参谋部系统架构和业务场景功能实现

将以一个基于微服务和云原生理念、深度集成人工智能组件、强调实时性与韧性的系统架构为基础,详细阐述如何落地“智能参谋部”的各项能力。这不是一个简单的软件堆叠,而是一个有机整合了数据、知识、模型、流程与人员的复杂体系。 系统愿景:“智能参谋部”——基于AI赋能的…

企业级RAG架构设计:从FAISS索引到HyDE优化的全链路拆解,金融/医疗领域RAG落地案例与避坑指南(附架构图)

本文较长&#xff0c;纯干货&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习内容&#xff0c;尽在聚客AI学院。 一. RAG技术概述 1.1 什么是RAG&#xff1f; RAG&#xff08;Retrieval-Augmented Generation&#xff0c;检索增强生成&#xff09; 是…

Spring Boot Validation实战详解:从入门到自定义规则

目录 一、Spring Boot Validation简介 1.1 什么是spring-boot-starter-validation&#xff1f; 1.2 核心优势 二、快速集成与配置 2.1 添加依赖 2.2 基础配置 三、核心注解详解 3.1 常用校验注解 3.2 嵌套对象校验 四、实战开发步骤 4.1 DTO类定义校验规则 4.2 Cont…

理清缓存穿透、缓存击穿、缓存雪崩、缓存不一致的本质与解决方案

在构建高性能系统中&#xff0c;缓存&#xff08;如Redis&#xff09; 是不可或缺的关键组件&#xff0c;它大幅减轻了数据库压力、加快了响应速度。然而&#xff0c;在高并发环境下&#xff0c;缓存也可能带来一系列棘手的问题&#xff0c;如&#xff1a;缓存穿透、缓存击穿、…

PyTorch_构建线性回归

使用 PyTorch 的 API 来手动构建一个线性回归的假设函数&#xff0c;数据加载器&#xff0c;损失函数&#xff0c;优化方法&#xff0c;绘制训练过程中的损失变化。 数据构建 import torch from sklearn.datasets import make_regression import matplotlib.pyplot as plt i…