【问题标题】:s3 urls - get bucket name and paths3 urls - 获取存储桶名称和路径
【发布时间】:2021-11-18 14:27:36
【问题描述】:

我有一个具有 aws s3 url 的变量

s3://bucket_name/folder1/folder2/file1.json

我想在一个变量中获取 bucket_name 并在另一个变量中休息,即 /folder1/folder2/file1.json。我尝试了正则表达式,可以得到如下的bucket_name,不知道是否有更好的方法。

m = re.search('(?<=s3:\/\/)[^\/]+', 's3://bucket_name/folder1/folder2/file1.json')
print(m.group(0))

我如何获得其余部分,即 - folder1/folder2/file1.json?

我检查了是否有 boto3 功能可以从 url 中提取 bucket_name 和 key,但找不到。

【问题讨论】:

    标签: python boto3


    【解决方案1】:

    由于它只是一个普通的 URL,你可以使用urlparse 来获取 URL 的所有部分。

    >>> from urlparse import urlparse
    >>> o = urlparse('s3://bucket_name/folder1/folder2/file1.json', allow_fragments=False)
    >>> o
    ParseResult(scheme='s3', netloc='bucket_name', path='/folder1/folder2/file1.json', params='', query='', fragment='')
    >>> o.netloc
    'bucket_name'
    >>> o.path
    '/folder1/folder2/file1.json'
    

    您可能必须按照下一个答案的建议从键中删除开头的斜杠。

    o.path.lstrip('/')
    

    随着 Python 3 urlparse 移动到 urllib.parse 所以使用:

    from urllib.parse import urlparse
    

    这是一个处理所有细节的类。

    try:
        from urlparse import urlparse
    except ImportError:
        from urllib.parse import urlparse
    
    
    class S3Url(object):
        """
        >>> s = S3Url("s3://bucket/hello/world")
        >>> s.bucket
        'bucket'
        >>> s.key
        'hello/world'
        >>> s.url
        's3://bucket/hello/world'
    
        >>> s = S3Url("s3://bucket/hello/world?qwe1=3#ddd")
        >>> s.bucket
        'bucket'
        >>> s.key
        'hello/world?qwe1=3#ddd'
        >>> s.url
        's3://bucket/hello/world?qwe1=3#ddd'
    
        >>> s = S3Url("s3://bucket/hello/world#foo?bar=2")
        >>> s.key
        'hello/world#foo?bar=2'
        >>> s.url
        's3://bucket/hello/world#foo?bar=2'
        """
    
        def __init__(self, url):
            self._parsed = urlparse(url, allow_fragments=False)
    
        @property
        def bucket(self):
            return self._parsed.netloc
    
        @property
        def key(self):
            if self._parsed.query:
                return self._parsed.path.lstrip('/') + '?' + self._parsed.query
            else:
                return self._parsed.path.lstrip('/')
    
        @property
        def url(self):
            return self._parsed.geturl()
    

    【讨论】:

    • 请注意,如果您的文件名包含#,在这种情况下,o.path 将不包含完整的密钥。 urlparse('s3://bucket_name/file #2.json').path == '/file '
    • @charlax 是否有允许任意文件名的解决方案(例如,包括#)?
    • 您可以使用allow_fragments=False。请参阅更新的答案。如果你也想支持?,你可以检查query是否设置并添加到最终结果中。
    • 嗯...怎么样:s3_filepath = "s3://bucket-name/some/key.txt"bucket, key = s3_filepath.replace("s3://", "").split(1)
    【解决方案2】:

    无需 urllib 或 re 的解决方案(也可以处理前面的斜杠):

    def split_s3_path(s3_path):
        path_parts=s3_path.replace("s3://","").split("/")
        bucket=path_parts.pop(0)
        key="/".join(path_parts)
        return bucket, key
    

    运行:

    bucket, key = split_s3_path("s3://my-bucket/some_folder/another_folder/my_file.txt")
    

    返回:

    bucket: my-bucket
    key: some_folder/another_folder/my_file.txt
    

    【讨论】:

    • 使用.partition("/") 可能比使用.split("/").join("/") 更好。 bucket, _, key = s3_path.replace("s3://","").partition("/") 此外,这是假设 s3:// 不会作为子字符串出现在路径本身中。没有理智的人会这样做,但也许攻击者可以利用这个?不确定。
    【解决方案3】:

    对于那些像我一样尝试使用 urlparse 来提取密钥和存储桶以便使用 boto3 创建对象的人。有一个重要的细节:从键的开头删除斜线

    from urlparse import urlparse
    o = urlparse('s3://bucket_name/folder1/folder2/file1.json')
    bucket = o.netloc
    key = o.path
    boto3.client('s3')
    client.put_object(Body='test', Bucket=bucket, Key=key.lstrip('/'))
    

    花了一段时间才意识到这一点,因为 boto3 不会抛出任何异常。

    【讨论】:

    • 感谢您提供有用的答案,我认为您使用了两次lstrip,一次是分配给key,另一次是在将key 传递给put_object 方法时。除非您的键有两个连续的斜杠,否则这可能无关紧要。这对于 s3 对象名称可能是可能的。
    • key为o.path,原回复中不包含。
    • 谢谢@RicardoMayerhofer!固定。
    【解决方案4】:

    使用一行内置字符串方法很容易完成...

    s3_filepath = "s3://bucket-name/and/some/key.txt"
    bucket, key = s3_filepath.replace("s3://", "").split("/", 1)
    

    【讨论】:

      【解决方案5】:

      如果你想用正则表达式来做,你可以这样做:

      >>> import re
      >>> uri = 's3://my-bucket/my-folder/my-object.png'
      >>> match = re.match(r's3:\/\/(.+?)\/(.+)', uri)
      >>> match.group(1)
      'my-bucket'
      >>> match.group(2)
      'my-folder/my-object.png'
      

      这样做的好处是您可以检查s3 方案,而不是在那里允许任何东西。

      【讨论】:

        【解决方案6】:

        这是一个不错的项目:

        s3path 是 aws s3 服务的 pathlib 扩展

        >>> from s3path import S3Path
        >>> path = S3Path.from_uri('s3://bucket_name/folder1/folder2/file1.json')
        >>> print(path.bucket)
        '/bucket_name'
        >>> print(path.key)
        'folder1/folder2/file1.json'
        >>> print(list(path.key.parents))
        [S3Path('folder1/folder2'), S3Path('folder1'), S3Path('.')]
        

        【讨论】:

        • 这行得通!确实是一个不错的项目。
        【解决方案7】:

        最近的一个选项是使用cloudpathlib,它为云服务(包括S3、Google Cloud Storage 和Azure Blob Storage)上的文件实现pathlib 函数。

        除了这些功能之外,还可以轻松获取 S3 路径的存储桶和密钥。

        from cloudpathlib import S3Path
        
        path = S3Path("s3://bucket_name/folder1/folder2/file1.json")
        
        path.bucket
        #> 'bucket_name'
        
        path.key
        #> 'folder1/folder2/file1.json'
        

        【讨论】:

          【解决方案8】:

          这里是使用正则表达式的单行:

          import re
          
          s3_path = "s3://bucket/path/to/key"
          
          bucket, key = re.match(r"s3:\/\/(.+?)\/(.+)", s3_path).groups()
          

          【讨论】:

            【解决方案9】:

            这可以顺利完成

            bucket_name, key = s3_uri[5:].split('/', 1)
            

            【讨论】:

              【解决方案10】:

              我使用以下正则表达式:

              ^(?:[s|S]3:\/\/)?([a-zA-Z0-9\._-]+)(?:\/)(.+)$
              

              如果匹配,则 S3 解析部分如下:

              • 匹配组 1 => S3 存储桶名称
              • 匹配组 2 => S3 对象名称

              此模式处理带或不带 s3:// uri 前缀的存储桶路径。

              如果要允许其他 legal 存储桶名称字符,请根据需要修改模式的 [a-zA-Z0-9_-] 部分以包含其他字符。

              完整的 JS 示例(Typescript 形式)

              const S3_URI_PATTERN = '^(?:[s|S]3:\\/\\/)?([a-zA-Z0-9\\._-]+)(?:\\/)(.+)$';
              
              export interface S3UriParseResult {
                bucket: string;
                name: string;
              }
              
              export class S3Helper {
                /**
                 *
                 * @param uri
                 */
                static parseUri(uri: string): S3UriParseResult {
                  const re = new RegExp(S3_URI_PATTERN);
                  const match = re.exec(uri);
                  if (!match || (match && match.length !== 3)) {
                    throw new Error('Invalid S3 object URI');
                  }
                  return {
                    bucket: match[1],
                    name: match[2],
                  };
                }
              }
              
              

              【讨论】:

                【解决方案11】:

                我做的最简单的是:

                s = 's3://bucket/path1/path2/file.txt'
                s1 = s.split('/', 3)
                bucket = s1[2]
                object_key = s1[3]
                

                【讨论】:

                • 欢迎来到 StackOverflow @Czar!您的解决方案不完整,因为它不返回“path1”和“path2”。
                • 我不确定你的意思。我刚刚测试了代码,我得到了object_key='path1/path2/file.txt'。此解决方案的优点是非常简单!
                猜你喜欢
                • 2021-08-02
                • 1970-01-01
                • 2020-11-10
                • 1970-01-01
                • 1970-01-01
                • 2019-10-22
                • 1970-01-01
                • 2011-02-13
                • 1970-01-01
                相关资源
                最近更新 更多