qidao123.com技术社区-IT企服评测·应用市场

标题: aws(学习笔记第四十课) image-content-search [打印本页]

作者: 宁睿    时间: 2025-5-3 19:25
标题: 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文件

这里配置了Environment,Author,Region等配置,比起写入cdk.py的python代码中,这里将配置数据写入yml文件中会更加清晰。
  1. Environment: Development
  2. Author: Mohsen
  3. Region: eu-central-1
  4. ProjectName: ImageContentSearch
  5. DeadLetterQueue:
  6.     MaxReceiveCount: 3
  7. Cognito:
  8.     SelfSignUp: True
  9.     DomainPrefix: image-content-search
  10.     AllowedOAuthScopes:
  11.         - phone
  12.         - email
  13.         - openid
  14.         - profile
  15. Database:
  16.     Name: images_labels
  17.     DeletionProtection: False
  18.     Scaling:
  19.         AutoPause: True
  20.         Min: 2
  21.         Max: 8
  22.         SecondsToAutoPause: 1800
  23. Functions:
  24.     DefaultSignedUrlExpirySeconds: "3600"
  25.     DefaultMaxApiCallAttempts: "5"
复制代码
2.1.2 yml文件文件解析

  1. with open("stack/config.yml", 'r') as stream:
  2.             configs = yaml.safe_load(stream)
  3. # for example, use configs in image_data_function
  4. image_data_function = Function(self, "ICS_IMAGE_DATA",
  5.             function_name="ICS_IMAGE_DATA",
  6.             runtime=Runtime.PYTHON_3_7,
  7.             timeout=Duration.seconds(5),
  8.             role=image_data_function_role,
  9.             environment={
  10.                 "DEFAULT_MAX_CALL_ATTEMPTS": configs["Functions"]["DefaultMaxApiCallAttempts"],
  11.                 "CLUSTER_ARN": database_cluster_arn,
  12.                 "CREDENTIALS_ARN": database_secret.secret_arn,
  13.                 "DB_NAME": database.database_name,
  14.                 "REGION": Aws.REGION
  15.                 },
  16.             handler="main.handler",
  17.             code=Code.from_asset("./src/imageData")
  18.         )
复制代码
2.2 创建s3 bucket

  1.         ### S3 core
  2.         images_S3_bucket = _s3.Bucket(self, "ICS_IMAGES")
  3.         images_S3_bucket.add_cors_rule(
  4.             allowed_methods=[_s3.HttpMethods.POST],
  5.             allowed_origins=["*"] # add API gateway web resource URL
  6.         )
复制代码
这里,必要从API gateway的domain进行跨域访问S3 bucket的AWS的url,因此必要CORS Cross-Origin Resource Share,是浏览器的一种安全机制,用于控制不同源(协议+域名+端口)之间的资源访问。
在之前的文章中介绍过。spring boot(学习笔记第五课) 自定义错误页,CORS(跨域支持)
2.3 创建API Gateway

  1. ### api gateway core
  2.         api_gateway = RestApi(self, 'ICS_API_GATEWAY', rest_api_name='ImageContentSearchApiGateway')
  3.         api_gateway_resource = api_gateway.root.add_resource(configs["ProjectName"])
  4.         api_gateway_landing_page_resource = api_gateway_resource.add_resource('web')
  5.         api_gateway_get_signedurl_resource = api_gateway_resource.add_resource('signedUrl')
  6.         api_gateway_image_search_resource = api_gateway_resource.add_resource('search')
复制代码

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

2.4.1 创建文件上传的api_gateway_resource的lambda函数

  1. ### landing page function
  2.         get_landing_page_function = Function(self, "ICS_GET_LANDING_PAGE",
  3.             function_name="ICS_GET_LANDING_PAGE",
  4.             runtime=Runtime.PYTHON_3_7,
  5.             handler="main.handler",
  6.             code=Code.from_asset("./src/landingPage"))
复制代码
2.4.2 配置文件上传的LambdaIntegration

  1. get_landing_page_integration = LambdaIntegration(
  2.             get_landing_page_function,
  3.             proxy=True,
  4.             integration_responses=[IntegrationResponse(
  5.                 status_code='200',
  6.                 response_parameters={
  7.                     'method.response.header.Access-Control-Allow-Origin': "'*'"
  8.                 }
  9.             )])
复制代码
留意,这里配置method.response.header.Access-Control-Allow-Origin以便答应其他domain过来的跨域的访问。但是,如果是生产情况,必要将*换成特定的domain
2.4.3 配置文件上传的API Gateway Method

  1.         api_gateway_landing_page_resource.add_method('GET', get_landing_page_integration,
  2.             method_responses=[MethodResponse(
  3.                 status_code='200',
  4.                 response_parameters={
  5.                     'method.response.header.Access-Control-Allow-Origin': True
  6.                 }
  7.             )])
复制代码
2.4.4 配置文件上传的页面代码


2.4.4.1 python的lambda handler

\src\landingPage\main.py
  1. # this function
  2. # gets the simple html page
  3. # updates the login page and logout page address
  4. # returns the content
  5. def handler(event, context):
  6.     login_page = event["headers"]["Referer"]
  7.     return {
  8.         'statusCode': 200,
  9.         'headers': {
  10.             'Content-Type': 'text/html'
  11.         },
  12.         'body': file_get_contents("index.html").replace('###loginPage###', login_page)
  13.     }
  14. def file_get_contents(filename):
  15.     with open(filename) as f:
  16.         return f.read()
复制代码
这里,直接将html的文件打开,进行返回
2.4.4.2 html的页面代码

\src\landingPage\index.html
  1. <html>
  2. <head>
  3.   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  4.   <meta name="viewport" content="width=device-width, initial-scale=1">
  5.   <style>
  6.     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}}
  7.     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}
  8.     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}
  9.   </style>
  10.   <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
  11.   <script>
  12.     var authData = window.location.hash.substring(1);
  13.     var idToken = authData.split('&').find((rec) => rec.split('=')[0] == 'id_token').split('=')[1]
  14.     var getSignedUrlEndpoint = window.location.href.split('#')[0].replace('web', 'signedUrl')
  15.     var searchImageEndpoint = window.location.href.split('#')[0].replace('web', 'search')
  16.     var loginPage = '###loginPage###';
  17.     var logoutPage = loginPage.replace('login', 'logout');
  18.     function logout() {
  19.       window.location.replace(logoutPage);
  20.     }
  21.     function getSignedUrl() {
  22.       $.ajax({
  23.           url: getSignedUrlEndpoint,
  24.           headers: { 'Authorization': idToken },
  25.           type: "GET",
  26.           contentType: 'application/json; charset=utf-8',
  27.           success: function (result) {
  28.             console.log(result);
  29.             $("#upload_image_form").attr('action', result.url);
  30.             $('input[name="key"]').val(result.fields.key);
  31.             $('input[name="X-Amz-Credential"]').val(result.fields['x-amz-credential']);
  32.             $('input[name="X-Amz-Algorithm"]').val(result.fields['x-amz-algorithm']);
  33.             $('input[name="X-Amz-Date"]').val(result.fields['x-amz-date']);
  34.             $('input[name="x-amz-security-token"]').val(result.fields['x-amz-security-token']);
  35.             $('input[name="Policy"]').val(result.fields.policy);
  36.             $('input[name="X-Amz-Signature"]').val(result.fields['x-amz-signature']);
  37.           },
  38.           error: function (error) {
  39.             console.log(error);
  40.             if (error.status == 401) {logout();}
  41.           }
  42.         });
  43.     }
  44.     function listImagesByLabel(outputTab, label, language, country) {
  45.       console.log('Finding images with label: ' + label);
  46.       var formData = language ? {label, language, country} : {label}
  47.       $.ajax({
  48.         url: searchImageEndpoint,
  49.         headers: { 'Authorization': idToken },
  50.         type: "POST",
  51.         data: {...formData, 'source': 'API'},
  52.         contentType: 'application/json; charset=utf-8',
  53.         success: function (results) {
  54.           console.log(results);
  55.           $(outputTab + " tr").remove();
  56.           $(outputTab + " th").remove();
  57.           if (results) {
  58.             $(outputTab).append( '<tr><th>Image ID</th></tr>' );
  59.             results.forEach(item => {
  60.               $(outputTab).append( '<tr><td>' + item.id + '</th></tr>' );
  61.             });
  62.           }
  63.         },
  64.         error: function (error) {
  65.           console.log(error.responseText, error.status);
  66.           if (error.status == 401) {logout();}
  67.         }
  68.       });
  69.     }
  70.     $(document).ready(function () {
  71.       if (window.location.hash) {
  72.         // getSignedUrl();
  73.       } else {
  74.         console.log('Authorization information from cognito is not found!');
  75.       }
  76.     });
  77.     function submitSearchQuery() {
  78.       event.preventDefault();
  79.       var language = $('#language').val();
  80.       var country = $('#country').val();
  81.       var label = $('input[name=label]').val();
  82.       listImagesByLabel('#search_image_result', label, language, country);
  83.     }
  84.     function openTab(evt, tabName) {
  85.       $("#upload_result").text('');
  86.       $("#upload_result").css("color", "black");
  87.       $("#file_select").val('');
  88.       if (tabName == 'upload') {
  89.         getSignedUrl();
  90.       }
  91.       if (tabName == 'report') {
  92.         listImagesByLabel('#report_image_result', 'offensive');
  93.       }
  94.       var i, tabcontent, tablinks;
  95.       tabcontent = document.getElementsByClassName("tabcontent");
  96.       for (i = 0; i < tabcontent.length; i++) {
  97.         tabcontent[i].style.display = "none";
  98.       }
  99.       tablinks = document.getElementsByClassName("tablinks");
  100.       for (i = 0; i < tablinks.length; i++) {
  101.         tablinks[i].className = tablinks[i].className.replace(" active", "");
  102.       }
  103.       document.getElementById(tabName).style.display = "block";
  104.       evt.currentTarget.className += " active";
  105.     }
  106.     function submitFileUpload() {
  107.       event.preventDefault();
  108.       var formData = new FormData();
  109.       var selectedFile = $('input[name="file"]')[0]
  110.       $('#upload_image_form *').filter(':input').filter(":hidden").each(function(k, v){
  111.         formData.append(v.name, v.defaultValue);
  112.       });
  113.       formData.append("file", selectedFile.files[0]);
  114.       $.ajax({
  115.           url: $("#upload_image_form").attr('action'),
  116.           type: 'POST',
  117.           data: formData,
  118.           success: function (data) {
  119.             $("#upload_result").text('The file has been successfully uploaded!');
  120.             $("#upload_result").css("color", "green");
  121.             getSignedUrl();
  122.           },
  123.           error: function(xhr, textStatus, errorThrown){
  124.             $("#upload_result").text('The file upload failed!');
  125.             $("#upload_result").css("color", "red");
  126.             console.log(textStatus);
  127.             console.log(errorThrown);
  128.           },
  129.           cache: false,
  130.           contentType: false,
  131.           processData: false
  132.       });
  133.     };
  134.   </script>
  135. </head>
  136. <body>
  137.   <div style="width: 50%; margin-left: 25%;">
  138.     <div class="tab" style="margin-top: 10px;">
  139.       <button class="tablinks" onclick="openTab(event, 'upload')" id="default_tab">Upload</button>
  140.       <button class="tablinks" onclick="openTab(event, 'search')">Search</button>
  141.       <button class="tablinks" onclick="openTab(event, 'report')">Report</button>
  142.       <button class="tablinks" onclick="logout()" style="float: right;">Logout</button>
  143.     </div>
  144.     <div id="upload" class="tabcontent">
  145.       <h3>Upload Image</h3>
  146.       <p>Select image to upload:</p>
  147.       <form id="upload_image_form" method="post" enctype="multipart/form-data">
  148.         <input type="hidden" name="key"/><br />
  149.         <input type="hidden" name="X-Amz-Credential"/>
  150.         <input type="hidden" name="X-Amz-Algorithm"/>
  151.         <input type="hidden" name="X-Amz-Date"/>
  152.         <input type="hidden" name="x-amz-security-token"/>
  153.         <input type="hidden" name="Policy"/>
  154.         <input type="hidden" name="X-Amz-Signature"/>
  155.         <input type="file" id="file_select" name="file"/> <br />
  156.         <input type="submit" class="submit" value="Upload" onclick="submitFileUpload()"/>
  157.       </form>
  158.       <p id="upload_result"></p>
  159.     </div>
  160.     <div id="search" class="tabcontent">
  161.       <h3>Search Labels</h3>
  162.       <form id="search_image_form" method="post">
  163.         <label >Language:</label>
  164.         <select name="language" id="language">
  165.           <option value="en">English</option>
  166.           <option value="tr">Turkish</option>
  167.           <option value="nl">Dutch</option>
  168.         </select>
  169.         <br />
  170.         <label >Country:</label>
  171.         <select name="country" id="country">
  172.           <option value="nl">Netherlands</option>
  173.         </select>
  174.         <br />
  175.         <label >Label to search:</label>
  176.         <input type="text" name="label"/><br />
  177.         <input class="submit" type="submit" value="Search" onclick="submitSearchQuery()"/>
  178.       </form>
  179.       <table id="search_image_result"></table>
  180.     </div>
  181.     <div id="report" class="tabcontent">
  182.       <h3>Report of offensive photos</h3>
  183.       <table id="report_image_result"></table>
  184.     </div>
  185.   </div>
  186.   <script>
  187.     document.getElementById("default_tab").click();
  188.   </script>
  189. </body>
  190. </html>
复制代码
2.5 配置cognito安全认证

  1. ### cognito
  2.         required_attribute = _cognito.StandardAttribute(required=True)
  3.         users_pool = _cognito.UserPool(self, "ICS_USERS_POOL",
  4.             auto_verify=_cognito.AutoVerifiedAttrs(email=True), #required for self sign-up
  5.             standard_attributes=_cognito.StandardAttributes(email=required_attribute), #required for self sign-up
  6.             self_sign_up_enabled=configs["Cognito"]["SelfSignUp"])
  7.         user_pool_app_client = _cognito.CfnUserPoolClient(self, "ICS_USERS_POOL_APP_CLIENT",
  8.             supported_identity_providers=["COGNITO"],
  9.             allowed_o_auth_flows=["implicit"],
  10.             allowed_o_auth_scopes=configs["Cognito"]["AllowedOAuthScopes"],
  11.             user_pool_id=users_pool.user_pool_id,
  12.             callback_urls=[api_gateway.url_for_path('/web')],
  13.             allowed_o_auth_flows_user_pool_client=True,
  14.             explicit_auth_flows=["ALLOW_REFRESH_TOKEN_AUTH"])
复制代码
这里表示/web必要认证,并且认证之后url将重定向到/web。
2.6 创建signedURL的API Gateway

2.6.1 创建signedURL的API Gateway

  1.         ### get signed URL function
  2.         get_signedurl_function = Function(self, "ICS_GET_SIGNED_URL",
  3.             function_name="ICS_GET_SIGNED_URL",
  4.             environment={
  5.                 "ICS_IMAGES_BUCKET": images_S3_bucket.bucket_name,
  6.                 "DEFAULT_SIGNEDURL_EXPIRY_SECONDS": configs["Functions"]["DefaultSignedUrlExpirySeconds"]
  7.             },
  8.             runtime=Runtime.PYTHON_3_7,
  9.             handler="main.handler",
  10.             code=Code.from_asset("./src/getSignedUrl"))
  11.         get_signedurl_integration = LambdaIntegration(
  12.             get_signedurl_function,
  13.             proxy=True,
  14.             integration_responses=[IntegrationResponse(
  15.                 status_code='200',
  16.                 response_parameters={
  17.                    'method.response.header.Access-Control-Allow-Origin': "'*'",
  18.                 }
  19.             )])
  20.         api_gateway_get_signedurl_authorizer = CfnAuthorizer(self, "ICS_API_GATEWAY_GET_SIGNED_URL_AUTHORIZER",
  21.             rest_api_id=api_gateway_get_signedurl_resource.api.rest_api_id,
  22.             name="ICS_API_GATEWAY_GET_SIGNED_URL_AUTHORIZER",
  23.             type="COGNITO_USER_POOLS",
  24.             identity_source="method.request.header.Authorization",
  25.             provider_arns=[users_pool.user_pool_arn])
  26.         get_signedurl_method = api_gateway_get_signedurl_resource.add_method('GET', get_signedurl_integration,
  27.             authorization_type=AuthorizationType.COGNITO,
  28.             method_responses=[MethodResponse(
  29.                 status_code='200',
  30.                 response_parameters={
  31.                     'method.response.header.Access-Control-Allow-Origin': True,
  32.                 }
  33.             )])
  34.         signedurl_custom_resource = typing.cast("aws_cloudformation.CfnCustomResource", get_signedurl_method.node.find_child('Resource'))
  35.         signedurl_custom_resource.add_property_override('AuthorizerId', api_gateway_get_signedurl_authorizer.ref)
  36.         images_S3_bucket.grant_put(get_signedurl_function, objects_key_pattern="new/*")
复制代码
2.6.2 创建signedURL的API Gateway处理的lambda

这里,从前端通报来的文件被put到S3 bucket。
  1. import json
  2. import boto3
  3. import logging
  4. import os
  5. import time
  6. import hashlib
  7. from botocore.exceptions import ClientError
  8. images_bucket = os.environ['ICS_IMAGES_BUCKET']
  9. default_signedurl_expiry_seconds = os.environ['DEFAULT_SIGNEDURL_EXPIRY_SECONDS']
  10. # this function
  11. # creates a pre-sighned URL for uploading image to S3 and returns it
  12. def handler(event, context):
  13.     uniquehash = hashlib.sha1("{}".format(time.time_ns()).encode('utf-8')).hexdigest()
  14.     result = create_presigned_post(images_bucket, "new/{}/{}".format(uniquehash[:2],uniquehash))
  15.     return {
  16.         'statusCode': 200,
  17.         'headers': {
  18.             'Content-Type': 'application/json; charset=UTF-8'
  19.         },
  20.         'body': json.dumps(result)
  21.     }
  22. def create_presigned_post(bucket_name, object_name, fields=None, conditions=None, expiration=default_signedurl_expiry_seconds):
  23.     s3_client = boto3.client('s3')
  24.     try:
  25.         response = s3_client.generate_presigned_post(bucket_name,
  26.             object_name,
  27.             Fields=fields,
  28.             Conditions=conditions,
  29.             ExpiresIn=int(expiration)
  30.         )
  31.     except ClientError as e:
  32.         logging.error(e)
  33.         return None
  34.     return response
复制代码
2.7 监视S3 bucket的lambda

2.7.1 监视架构


2.7.2 创建lmabda

  1. ### image massage function
  2.         image_massage_function = Function(self, "ICS_IMAGE_MASSAGE",
  3.             function_name="ICS_IMAGE_MASSAGE",
  4.             timeout=Duration.seconds(6),
  5.             runtime=Runtime.PYTHON_3_7,
  6.             environment={"ICS_IMAGE_MASSAGE": image_queue.queue_name},
  7.             handler="main.handler",
  8.             code=Code.from_asset("./src/imageMassage"))
  9.         images_S3_bucket.grant_write(image_massage_function, "processed/*")
  10.         images_S3_bucket.grant_delete(image_massage_function, "new/*")
  11.         images_S3_bucket.grant_read(image_massage_function, "new/*")
  12.         new_image_added_notification = _s3notification.LambdaDestination(image_massage_function)
  13.         images_S3_bucket.add_event_notification(_s3.EventType.OBJECT_CREATED,
  14.             new_image_added_notification,
  15.             _s3.NotificationKeyFilter(prefix="new/")
  16.             )
  17.         image_queue.grant_send_messages(image_massage_function)
复制代码
2.7.2 S3 bucket监视的lmabda代码

  1. def handler(event, context):
  2.     s3 = boto3.resource('s3')
  3.     for record in event['Records']:
  4.         newKey = record['s3']['object']['key']
  5.         bucket = record['s3']['bucket']['name']
  6.         name = bucket.split("/")[-1]
  7.         localfile = "/tmp/{}".format(name)
  8.         # download the file
  9.         new_key_obj = s3.Object(bucket, newKey)
  10.         new_key_obj.download_file(localfile)
  11.         # calc hash
  12.         image_SHA1 = getSha1(localfile)
  13.         # check if not exist
  14.         processed_key = "processed/{}/{}".format(image_SHA1[:2], image_SHA1)
  15.         key_is_processed = isS3ObjectExist(bucket, processed_key)
  16.         if key_is_processed: continue
  17.         # add to the queue
  18.         message = json.dumps({
  19.             "image": processed_key,
  20.             "original_key": newKey,
  21.             "original_last_modified": new_key_obj.last_modified,
  22.             "etag": new_key_obj.e_tag
  23.         }, default=str)
  24.         queue = sqs.get_queue_by_name(QueueName=queue_name)
  25.         response = queue.send_message(MessageBody=message)
  26.         logger.info("Message {} has been sent.".format(response.get('MessageId')))
  27.         #move the image
  28.         s3.Object(bucket, processed_key).copy_from(CopySource="{}/{}".format(bucket,newKey))
  29.         new_key_obj.delete()
  30.         # delete local file
  31.         os.remove(localfile)
  32.     return True
  33. def isS3ObjectExist(bucket, key):
  34.     s3 = boto3.resource('s3')
  35.     try:
  36.         s3.Object(bucket,key)
  37.         return False
  38.     except botocore.exceptions.ClientError as e:
  39.         if e.response['Error']['Code'] == "404":
  40.             return True
  41.         else:
  42.             raise e
  43. def getSha1(filepath):
  44.     sha1 = hashlib.sha1()
  45.     with open(filepath, 'rb') as f:
  46.         while True:
  47.             data = f.read(65536) # read in 64kb chunks
  48.             if not data: break
  49.             sha1.update(data)
  50.     return sha1.hexdigest()
复制代码
2.8 创建lambda对图像进行分析

2.8.1 图像分析架构


2.8.1 图像分析lambda函数

  1. def handler(event, context):
  2.     for record in event['Records']:
  3.         # receiptHandle = record['receiptHandle']
  4.         body = record['body']
  5.         message = json.loads(body)
  6.         bucket = os.environ['ICS_IMAGES_BUCKET']
  7.         key = message['image']
  8.         # original_key = message['original_key']
  9.         # original_last_modified = message['original_last_modified']
  10.         # etag = message['etag']
  11.         logger.info('Processing {}.'.format(key))
  12.         detected_labels = rekognition_client.detect_labels(
  13.             Image={'S3Object': {'Bucket': bucket, 'Name': key}},
  14.             MaxLabels=20,
  15.             MinConfidence=85)
  16.         detected_unsafe_contents = rekognition_client.detect_moderation_labels(
  17.             Image={'S3Object': {'Bucket': bucket, 'Name': key}})
  18.         object_labels = []
  19.         for l in detected_labels['Labels']:
  20.             object_labels.append(l['Name'].lower()) # add objects in image
  21.         for l in detected_unsafe_contents['ModerationLabels']:
  22.             if ('offensive' not in object_labels): object_labels.append("offensive") #label image as offensive
  23.             object_labels.append(l['Name'].lower())
  24.         image_id = key.split("/")[-1]
  25.         response = events_client.put_events(
  26.             Entries=[
  27.                 {
  28.                     'Source': "EventBridge",
  29.                     'Resources': [
  30.                         context.invoked_function_arn,
  31.                     ],
  32.                     'DetailType': 'images_labels',
  33.                     'Detail': json.dumps({"labels": object_labels, "image_id": image_id}),
  34.                     'EventBusName': event_bus_name
  35.                 },
  36.             ]
  37.         )
  38.         if response["FailedEntryCount"] == 1:
  39.             raise Exception(f'Failed entry observed. Count: {response["Entries"]}')
复制代码
2.9 创建图像分析数据生存的数据库

2.9.1 创建数据库的密码

  1. ### database
  2.         database_secret = _secrets_manager.Secret(self, "ICS_DATABASE_SECRET",
  3.             secret_name="rds-db-credentials/image-content-search-rds-secret",
  4.             generate_secret_string=_secrets_manager.SecretStringGenerator(
  5.                 generate_string_key='password',
  6.                 secret_string_template='{"username": "dba"}',
  7.                 exclude_punctuation=True,
  8.                 exclude_characters='/@" \\\'',
  9.                 require_each_included_type=True
  10.             )
  11.         )
复制代码
2.9.2 创建数据库

  1. database = _rds.CfnDBCluster(self, "ICS_DATABASE",
  2.             engine=_rds.DatabaseClusterEngine.aurora_mysql(version=_rds.AuroraMysqlEngineVersion.VER_5_7_12).engine_type,
  3.             engine_mode="serverless",
  4.             database_name=configs["Database"]["Name"],
  5.             enable_http_endpoint=True,
  6.             deletion_protection=configs["Database"]["DeletionProtection"],
  7.             master_username=database_secret.secret_value_from_json("username").to_string(),
  8.             master_user_password=database_secret.secret_value_from_json("password").to_string(),
  9.             scaling_configuration=_rds.CfnDBCluster.ScalingConfigurationProperty(
  10.                 auto_pause=configs["Database"]["Scaling"]["AutoPause"],
  11.                 min_capacity=configs["Database"]["Scaling"]["Min"],
  12.                 max_capacity=configs["Database"]["Scaling"]["Max"],
  13.                 seconds_until_auto_pause=configs["Database"]["Scaling"]["SecondsToAutoPause"]
  14.             ),
  15.         )
复制代码
2.9.3 将数据库密码和数据库绑定attachment

  1.         database_cluster_arn = "arn:aws:rds:{}:{}:cluster:{}".format(Aws.REGION, Aws.ACCOUNT_ID, database.ref)
  2.         secret_target = _secrets_manager.CfnSecretTargetAttachment(self,"ICS_DATABASE_SECRET_TARGET",
  3.             target_type="AWS::RDS::DBCluster",
  4.             target_id=database.ref,
  5.             secret_id=database_secret.secret_arn
  6.         )
  7.         secret_target.node.add_dependency(database)
复制代码
2.10 创建数据库的lambda function

2.10.1 创建数据库访问role

  1.         ### database function
  2.         image_data_function_role = _iam.Role(self, "ICS_IMAGE_DATA_FUNCTION_ROLE",
  3.             role_name="ICS_IMAGE_DATA_FUNCTION_ROLE",
  4.             assumed_by=_iam.ServicePrincipal("lambda.amazonaws.com"),
  5.             managed_policies=[
  6.                 _iam.ManagedPolicy.from_aws_managed_policy_name("service-role/AWSLambdaVPCAccessExecutionRole"),
  7.                 _iam.ManagedPolicy.from_aws_managed_policy_name("service-role/AWSLambdaBasicExecutionRole"),
  8.                 _iam.ManagedPolicy.from_aws_managed_policy_name("AmazonRDSDataFullAccess")
  9.             ]
  10.         )
复制代码
2.10.2 创建image_data_function


  1.        image_data_function = Function(self, "ICS_IMAGE_DATA",
  2.             function_name="ICS_IMAGE_DATA",
  3.             runtime=Runtime.PYTHON_3_7,
  4.             timeout=Duration.seconds(5),
  5.             role=image_data_function_role,
  6.             environment={
  7.                 "DEFAULT_MAX_CALL_ATTEMPTS": configs["Functions"]["DefaultMaxApiCallAttempts"],
  8.                 "CLUSTER_ARN": database_cluster_arn,
  9.                 "CREDENTIALS_ARN": database_secret.secret_arn,
  10.                 "DB_NAME": database.database_name,
  11.                 "REGION": Aws.REGION
  12.                 },
  13.             handler="main.handler",
  14.             code=Code.from_asset("./src/imageData")
  15.         )
复制代码
2.10.3 创建image search function


  1.         image_search_integration = LambdaIntegration(
  2.             image_data_function,
  3.             proxy=True,
  4.             integration_responses=[IntegrationResponse(
  5.                 status_code='200',
  6.                 response_parameters={
  7.                    'method.response.header.Access-Control-Allow-Origin': "'*'",
  8.                 }
  9.             )])
  10.         api_gateway_image_search_authorizer = CfnAuthorizer(self, "ICS_API_GATEWAY_IMAGE_SEARCH_AUTHORIZER",
  11.             rest_api_id=api_gateway_image_search_resource.api.rest_api_id,
  12.             name="ICS_API_GATEWAY_IMAGE_SEARCH_AUTHORIZER",
  13.             type="COGNITO_USER_POOLS",
  14.             identity_source="method.request.header.Authorization",
  15.             provider_arns=[users_pool.user_pool_arn])
  16.         search_integration_method = api_gateway_image_search_resource.add_method('POST', image_search_integration,
  17.             authorization_type=AuthorizationType.COGNITO,
  18.             method_responses=[MethodResponse(
  19.                 status_code='200',
  20.                 response_parameters={
  21.                     'method.response.header.Access-Control-Allow-Origin': True,
  22.                 }
  23.             )])
  24.         search_integration_custom_resource = typing.cast("aws_cloudformation.CfnCustomResource", search_integration_method.node.find_child('Resource'))
  25.         search_integration_custom_resource.add_property_override('AuthorizerId', api_gateway_image_search_authorizer.ref)
复制代码

2.10.4 创建image db的schema

  1.         ### custom resource
  2.         lambda_provider = Provider(self, 'ICS_IMAGE_DATA_PROVIDER',
  3.             on_event_handler=image_data_function
  4.         )
  5.         CustomResource(self, 'ICS_IMAGE_DATA_RESOURCE',
  6.             service_token=lambda_provider.service_token,
  7.             pascal_case_properties=False,
  8.             resource_type="Custom::SchemaCreation",
  9.             properties={
  10.                 "source": "Cloudformation"
  11.             }
  12.         )
复制代码

2.10.5 创建image db的生存lambda


image_analyzer_function生存分析结果到event_bus之中,这里继续将event_rule.add_target(_event_targets.LambdaFunction(image_data_function)),之后image_data_function会将数据生存到数据库。
  1.         ### event bridge
  2.         event_bus = _events.EventBus(self, "ICS_IMAGE_CONTENT_BUS", event_bus_name="ImageContentBus")
  3.         event_rule = _events.Rule(self, "ICS_IMAGE_CONTENT_RULE",
  4.             rule_name="ICS_IMAGE_CONTENT_RULE",
  5.             description="The event from image analyzer to store the data",
  6.             event_bus=event_bus,
  7.             event_pattern=_events.EventPattern(resources=[image_analyzer_function.function_arn]),
  8.         )
  9.         event_rule.add_target(_event_targets.LambdaFunction(image_data_function))
复制代码

3 执行cdk

TODO

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 qidao123.com技术社区-IT企服评测·应用市场 (https://dis.qidao123.com/) Powered by Discuz! X3.4