【问题标题】:How list Amazon S3 bucket contents by modified date?如何按修改日期列出 Amazon S3 存储桶内容?
【发布时间】:2018-01-07 19:59:03
【问题描述】:

大多数情况下,我们将文件加载到通用 S3 存储桶中,因此很难找出其中的数据。

如何查看在特定日期上传的对象?

【问题讨论】:

    标签: amazon-web-services amazon-s3 aws-cli


    【解决方案1】:

    一种解决方案可能是使用s3api。如果您的对象少于 1000 个,它很容易工作,否则您需要使用分页。

    s3api 可以列出所有对象,并且具有在 s3 中导入的键的 lastmodified 属性的属性。然后可以对其进行排序,查找日期之后或之前的文件,匹配日期...

    运行此类选项的示例

    1. 给定日期的所有文件
    DATE=$(date +%Y-%m-%d)
    bucket=test-bucket-fh
    aws s3api list-objects-v2 --bucket "$bucket" \
        --query 'Contents[?contains(LastModified, `'"$DATE"'`)]'
    
    1. 特定日期之后的所有文件
    SINCE=`date --date '-2 weeks +2 days' +%F 2>/dev/null || date -v '-2w' -v '+2d' +%F`
    #      ^^^^ GNU style                                    ^^^^ BSD style
    bucket=test-bucket-fh
    aws s3api list-objects-v2 --bucket "$bucket" \
        --query 'Contents[?LastModified > `'"$SINCE"'`]'
    

    s3api 将返回一个few metadata,以便您可以过滤特定元素

    DATE=$(date +%Y-%m-%d)
    bucket=test-bucket-fh
    aws s3api list-objects-v2 --bucket "$bucket" \
        --query 'Contents[?contains(LastModified, `'"$DATE"'`)].Key'
    

    【讨论】:

    • 这应该是一个选择的答案。其他答案建议ls桶中的对象然后排序,这将是昂贵的。
    • 这很有希望,但是当我在 bash 中运行导出命令时,我收到错误消息 date: invalid option -- 'v'
    • @Tim 这将取决于您拥有的date 程序,您可以检查man date 以获得正确的标志,您也可以参考stackoverflow.com/questions/1706882/… 以获得不同的日期风格跨度>
    • 感谢@FrédéricHenri。我有“日期(GNU coreutils)8.22”。我找到了另一种解决方法,我将把它作为答案发布。
    • @NicholasPorter awscli 的--query 选项是在客户端实现的,不是吗?我看不出这种方法比建议的其他方法便宜多少。
    【解决方案2】:

    在给定日期搜索

    aws s3api list-objects-v2 --bucket BUCKET_NAME --query 'Contents[?contains(LastModified, `YYYY-MM-DD`)].Key'
    

    从某个日期到今天的搜索

    aws s3api list-objects-v2 --bucket BUCKET_NAME  --query 'Contents[?LastModified>=`YYYY-MM-DD`].Key'
    

    您可以选择从查询末尾删除 .Key 以从 s3 对象中获取所有元数据字段

    【讨论】:

    • 这是最好的答案。这很简单,而且很有效
    • 请注意,如果要查询存储桶中的特定“文件夹”,请使用--prefix 选项。例如。 --prefix "folder/subfolder"
    • 你拯救了我的一天,伙计?
    【解决方案3】:

    如果它对将来的任何人有所帮助,这里有一个 python 程序,它允许您按一组前缀、后缀和/或上次修改日期进行过滤。请注意,您需要正确设置 aws 凭据才能使用 boto3。 请注意,这支持包含超过 1000 个键的前缀。

    用法:

    python save_keys_to_file.py -b 'bucket_name' -p some/prefix -s '.txt' '.TXT' -f '/Path/To/Some/File/test_keys.txt' -n '2018-1-1' -x '2018-2-1'
    

    代码文件名:save_keys_to_file.py

    import argparse
    import boto3
    import dateutil.parser
    import logging
    import pytz
    from collections import namedtuple
    
    logger = logging.getLogger(__name__)
    
    
    Rule = namedtuple('Rule', ['has_min', 'has_max'])
    last_modified_rules = {
        Rule(has_min=True, has_max=True):
            lambda min_date, date, max_date: min_date <= date <= max_date,
        Rule(has_min=True, has_max=False):
            lambda min_date, date, max_date: min_date <= date,
        Rule(has_min=False, has_max=True):
            lambda min_date, date, max_date: date <= max_date,
        Rule(has_min=False, has_max=False):
            lambda min_date, date, max_date: True,
    }
    
    
    def get_s3_objects(bucket, prefixes=None, suffixes=None, last_modified_min=None, last_modified_max=None):
        """
        Generate the objects in an S3 bucket. Adapted from:
        https://alexwlchan.net/2017/07/listing-s3-keys/
    
        :param bucket: Name of the S3 bucket.
        :ptype bucket: str
        :param prefixes: Only fetch keys that start with these prefixes (optional).
        :ptype prefixes: tuple
        :param suffixes: Only fetch keys that end with thes suffixes (optional).
        :ptype suffixes: tuple
        :param last_modified_min: Only yield objects with LastModified dates greater than this value (optional).
        :ptype last_modified_min: datetime.date
        :param last_modified_max: Only yield objects with LastModified dates greater than this value (optional).
        :ptype last_modified_max: datetime.date
    
        :returns: generator of dictionary objects
        :rtype: dict https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Client.list_objects
        """
        if last_modified_min and last_modified_max and last_modified_max < last_modified_min:
            raise ValueError(
                "When using both, last_modified_max: {} must be greater than last_modified_min: {}".format(
                    last_modified_max, last_modified_min
                )
            )
        # Use the last_modified_rules dict to lookup which conditional logic to apply
        # based on which arguments were supplied
        last_modified_rule = last_modified_rules[bool(last_modified_min), bool(last_modified_max)]
    
        if not prefixes:
            prefixes = ('',)
        else:
            prefixes = tuple(set(prefixes))
        if not suffixes:
            suffixes = ('',)
        else:
            suffixes = tuple(set(suffixes))
    
        s3 = boto3.client('s3')
        kwargs = {'Bucket': bucket}
    
        for prefix in prefixes:
            kwargs['Prefix'] = prefix
            while True:
                # The S3 API response is a large blob of metadata.
                # 'Contents' contains information about the listed objects.
                resp = s3.list_objects_v2(**kwargs)
                for content in resp.get('Contents', []):
                    last_modified_date = content['LastModified']
                    if (
                        content['Key'].endswith(suffixes) and
                        last_modified_rule(last_modified_min, last_modified_date, last_modified_max)
                    ):
                        yield content
    
                # The S3 API is paginated, returning up to 1000 keys at a time.
                # Pass the continuation token into the next response, until we
                # reach the final page (when this field is missing).
                try:
                    kwargs['ContinuationToken'] = resp['NextContinuationToken']
                except KeyError:
                    break
    
    
    def get_s3_keys(bucket, prefixes=None, suffixes=None, last_modified_min=None, last_modified_max=None):
        """
        Generate the keys in an S3 bucket.
    
        :param bucket: Name of the S3 bucket.
        :ptype bucket: str
        :param prefixes: Only fetch keys that start with these prefixes (optional).
        :ptype prefixes: tuple
        :param suffixes: Only fetch keys that end with thes suffixes (optional).
        :ptype suffixes: tuple
        :param last_modified_min: Only yield objects with LastModified dates greater than this value (optional).
        :ptype last_modified_min: datetime.date
        :param last_modified_max: Only yield objects with LastModified dates greater than this value (optional).
        :ptype last_modified_max: datetime.date
        """
        for obj in get_s3_objects(bucket, prefixes, suffixes, last_modified_min, last_modified_max):
            yield obj['Key']
    
    
    def valid_datetime(date):
        if date is None:
            return date
        try:
            utc = pytz.UTC
            return utc.localize(dateutil.parser.parse(date))
        except Exception:
            raise argparse.ArgumentTypeError("Could not parse value: '{}' to type datetime".format(date))
    
    
    def main():
        FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        logging.basicConfig(format=FORMAT)
        logger.setLevel(logging.DEBUG)
    
        parser = argparse.ArgumentParser(description='List keys in S3 bucket for prefix')
        parser.add_argument('-b', '--bucket', help='S3 Bucket')
        parser.add_argument('-p', '--prefixes', nargs='+', help='Filter s3 keys by a set of prefixes')
        parser.add_argument('-s', '--suffixes', nargs='*', help='Filter s3 keys by a set of suffixes')
        parser.add_argument('-n', '--last_modified_min', default=None, type=valid_datetime, help='Filter s3 content by minimum last modified date')
        parser.add_argument('-x', '--last_modified_max', default=None, type=valid_datetime, help='Filter s3 content by maximum last modified date')
        parser.add_argument('-f', '--file', help='Optional: file to write keys to.', default=None)
    
        args = parser.parse_args()
        logger.info(args)
        keys = get_s3_keys(args.bucket, args.prefixes, args.suffixes, args.last_modified_min, args.last_modified_max)
    
        open_file = open(args.file, 'w') if args.file else None
        try:
            counter = 0
            for key in keys:
                print(key, file=open_file)
                counter += 1
        finally:
            open_file.close()
    
        logger.info('Retrieved {} keys'.format(counter))
    
    
    if __name__ == '__main__':
        main()
    

    【讨论】:

      【解决方案4】:

      顺便说一句,如果你想在日期之间搜索,这适用于 Windows

      aws s3api list-objects-v2 --max-items 10 --bucket "BUCKET" --query "Contents[?LastModified&gt;='2019-10-01 00:00:00'] | [?LastModified&lt;='2019-10-30 00:00:00'].{ Key: Key, Size: Size, LastModified: LastModified }"

      【讨论】:

      • 使用这种方法,我必须添加--prefix 标志才能进入嵌套目录。问题:如何从密钥中删除前缀?我只想要文件名而不是"Key": "parent-dir1/parent-dir2/file_name"
      【解决方案5】:

      以下命令适用于 Linux。

      aws s3 ls --recursive s3:// <your s3 path here> | awk '$1 > "2018-10-13 00:00:00" {print $0}' | sort -n
      

      希望对你有帮助!!!

      【讨论】:

      • 请注意,如果存储桶中有很多对象,这种方法会很昂贵。您仍然需要为列出对象付费。查看下面的@frédéric-henri 答案。
      • 同意上面的评论,如果桶中有很多对象,这将是一种昂贵的方法
      【解决方案6】:

      这不是通用解决方案,但在您的对象根据日期命名时会很有帮助 - 例如 CloudTrail 日志。例如,我想要一个 2019 年 6 月创建的对象列表。

      aws s3api list-objects-v2 --bucket bucketname --prefix path/2019-06
      

      这会在服务器端进行过滤。使用“查询”参数的缺点是它会下载大量数据以在客户端进行过滤。这意味着可能需要大量的 API 调用(这会花钱),以及从 AWS 传出的额外数据,而您需要为此付费。

      来源:https://github.com/aws/aws-sdk-js/issues/2543

      【讨论】:

        【解决方案7】:

        如果为存储桶启用了版本控制,并且您希望在特定日期之后恢复最新删除的对象,则此命令是:

        $ aws s3api list-object-versions --bucket mybucket --prefix myprefix/ --output json --query 'DeleteMarkers[?LastModified>=`2020-07-07T00:00:00` && IsLatest==`true`].[Key,VersionId]' | jq -r '.[] |  "--key '\''" + .[0] +  "'\'' --version-id " + .[1]' |xargs -L1 aws s3api delete-object --bucket mybucket
        

        这意味着你已经安装了aws cli(我使用了 v. 2.0.30)和jq


        如果您想在删除之前确定一切正常,只需在aws 之前使用echo

        $ aws s3api list-object-versions --bucket mybucket --prefix myprefix/ --output json --query 'DeleteMarkers[?LastModified>=`2020-07-07T00:00:00` && IsLatest==`true`].[Key,VersionId]' | jq -r '.[] |  "--key '\''" + .[0] +  "'\'' --version-id " + .[1]' |xargs -L1 echo aws s3api delete-object --bucket mybucket > files.txt
        

        请注意,由于echo,引号将无法正确应用并保存在没有它的文件中。如果路径中没有空格,那没关系。 您可以检查该文件,如果一切正常,请以这种方式运行:

        $ cat files.txt | bash
        

        【讨论】:

          【解决方案8】:

          似乎没有 API 可让您在服务器端按修改日期进行过滤。所有过滤似乎都发生在客户端,因此无论您使用什么客户端(s3api、boto3 等),如果您必须对大量文件执行此操作,都会很慢。除非您可以通过在不同的子文件夹上运行列表操作来并行化该扫描,否则没有好的选择,但这绝对不适用于很多情况。

          我发现实际上使您能够按修改日期过滤大量文件的唯一选项是使用 AWS S3 清单 - https://docs.aws.amazon.com/AmazonS3/latest/userguide/storage-inventory.html。这样,AWS 为您运行 S3 文件索引并将文件的元数据(即文件路径、上次修改日期、大小等)存储在指定的 S3 位置。您可以轻松地使用它来按修改日期进行过滤。

          【讨论】:

            猜你喜欢
            • 2022-07-14
            • 1970-01-01
            • 2016-09-18
            • 1970-01-01
            • 2021-01-19
            • 1970-01-01
            • 2020-11-05
            • 2015-07-06
            • 2016-08-25
            相关资源
            最近更新 更多