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.pswp.cn/web/78764.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/78764.shtml
英文地址,請注明出處:http://en.pswp.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…