首页 > 解决方案 > 如何在 aws cdk 中使用自定义资源将文件上传到 s3 存储桶

问题描述

创建后,我需要将 zip 文件上传到 s3 存储桶。我知道 s3_​​deployment 包,但它不适合我的用例,因为我需要在堆栈创建时只上传一次文件。s3_deployment 包会在每次更新时上传 zip。

我定义了以下自定义资源,但是我不确定如何将文件主体传递给自定义资源。我尝试以二进制模式打开文件,但返回错误。

app_data_bootstrap = AwsCustomResource(self, "BootstrapData",
    on_create={
        "service": "S3",
        "action": "putObject",
        "parameters": {
            "Body": open('app_data.zip', 'rb'),
            "Bucket": f"my-app-data",
            "Key": "app_data.zip",
        },
        "physical_resource_id": PhysicalResourceId.of("BootstrapDataBucket")
    },
    policy=AwsCustomResourcePolicy.from_sdk_calls(resources=AwsCustomResourcePolicy.ANY_RESOURCE)
)

标签: aws-cdk

解决方案


我认为这是不可能的,除非您编写自定义脚本并在cdk deploy将本地文件上传到中间 S3 存储桶之前运行。然后,您可以编写一个自定义资源,将事件中的中间存储桶的内容复制on_create到通过 CDK 创建的存储桶。

s3_deployment从CDK 文档中阅读此段落:

这就是幕后发生的事情:

  1. 部署此堆栈时(通过 cdk deploy 或通过 CI/CD),本地 website-dist 目录的内容将被存档并上传到中间资产存储桶。如果有多个来源,它们将被单独上传。

  2. BucketDeployment 构造将 Custom::CDKBucketDeployment 类型的自定义 CloudFormation 资源综合到模板中。源存储桶/键设置为指向资产存储桶。

  3. 自定义资源下载 .zip 存档,将其解压缩并针对目标存储桶(在本例中为 websiteBucket)发出 aws s3 sync --delete。如果有多个源,则将在此步骤下载源并在部署前合并。

因此,为了复制第 1 步,您必须编写一个小脚本来创建中间存储桶并将本地文件上传到其中。该脚本的示例可以是这样的:

#!/bin/sh
aws s3 mb <intermediary_bucket> --region <region_name>
aws s3 sync <intermediary_bucket> s3://<your_bucket_name>

然后你的自定义资源可以是这样的:

*请注意,这适用于复制一个对象,您可以更改代码以复制多个对象。

import json
import boto3
import cfnresponse

def lambda_handler(event, context):
    print('Received request:\n%s' % json.dumps(event, indent=4))

    resource_properties = event['ResourceProperties']

    if event['RequestType'] in ['Create']: #What happens when resource is created
        try:
            s3 = boto3.resource('s3')
            copy_source = {
                'Bucket': 'intermediary_bucket',
                'Key': 'path/to/filename.extension'
            }
            bucket = s3.Bucket('otherbucket')
            obj = bucket.Object('otherkey')
            obj.copy(copy_source)

        except:
            cfnresponse.send(event, context, cfnresponse.FAILED, {})
            raise
        else:
            cfnresponse.send(event, context, cfnresponse.SUCCESS,
                             {'FileContent': response['fileContent'].decode('utf-8')})
    elif event['RequestType'] == 'Delete': # What happens when resource is deleted
        cfnresponse.send(event, context, cfnresponse.SUCCESS, {})

所有这一切的替代方法是在 AWS CDK 的Github 存储库中打开一个问题,并要求他们添加您的用例。


推荐阅读