kopia lustrzana https://github.com/simonw/s3-credentials
get-objects progress bar, closes #78
rodzic
af93f54dbb
commit
047020a289
|
@ -187,6 +187,7 @@ Options:
|
||||||
-o, --output DIRECTORY Write to this directory instead of one matching the
|
-o, --output DIRECTORY Write to this directory instead of one matching the
|
||||||
bucket name
|
bucket name
|
||||||
-p, --pattern TEXT Glob patterns for files to download, e.g. '*/*.js'
|
-p, --pattern TEXT Glob patterns for files to download, e.g. '*/*.js'
|
||||||
|
-s, --silent Don't show progress bar
|
||||||
--access-key TEXT AWS access key ID
|
--access-key TEXT AWS access key ID
|
||||||
--secret-key TEXT AWS secret access key
|
--secret-key TEXT AWS secret access key
|
||||||
--session-token TEXT AWS session token
|
--session-token TEXT AWS session token
|
||||||
|
|
|
@ -393,6 +393,8 @@ You can pass one or more `--pattern` or `-p` options to download files matching
|
||||||
|
|
||||||
Here the `*` wildcard will match any sequence of characters, including `/`. `?` will match a single character.
|
Here the `*` wildcard will match any sequence of characters, including `/`. `?` will match a single character.
|
||||||
|
|
||||||
|
A progress bar will be shown by default. Use `-s` or `--silent` to hide it.
|
||||||
|
|
||||||
## set-cors-policy and get-cors-policy
|
## set-cors-policy and get-cors-policy
|
||||||
|
|
||||||
You can set the [CORS policy](https://docs.aws.amazon.com/AmazonS3/latest/userguide/cors.html) for a bucket using the `set-cors-policy` command. S3 CORS policies are set at the bucket level - they cannot be set for individual items.
|
You can set the [CORS policy](https://docs.aws.amazon.com/AmazonS3/latest/userguide/cors.html) for a bucket using the `set-cors-policy` command. S3 CORS policies are set at the bucket level - they cannot be set for individual items.
|
||||||
|
|
|
@ -1052,8 +1052,9 @@ def get_object(bucket, key, output, **boto_options):
|
||||||
multiple=True,
|
multiple=True,
|
||||||
help="Glob patterns for files to download, e.g. '*/*.js'",
|
help="Glob patterns for files to download, e.g. '*/*.js'",
|
||||||
)
|
)
|
||||||
|
@click.option("silent", "-s", "--silent", is_flag=True, help="Don't show progress bar")
|
||||||
@common_boto3_options
|
@common_boto3_options
|
||||||
def get_objects(bucket, keys, output, patterns, **boto_options):
|
def get_objects(bucket, keys, output, patterns, silent, **boto_options):
|
||||||
"""
|
"""
|
||||||
Download multiple objects from an S3 bucket
|
Download multiple objects from an S3 bucket
|
||||||
|
|
||||||
|
@ -1076,35 +1077,67 @@ def get_objects(bucket, keys, output, patterns, **boto_options):
|
||||||
|
|
||||||
# If user specified keys and no patterns, use the keys they specified
|
# If user specified keys and no patterns, use the keys they specified
|
||||||
keys_to_download = list(keys)
|
keys_to_download = list(keys)
|
||||||
|
key_sizes = {}
|
||||||
|
|
||||||
|
if keys and not silent:
|
||||||
|
# Get sizes of those keys for progress bar
|
||||||
|
for key in keys:
|
||||||
|
try:
|
||||||
|
key_sizes[key] = s3.head_object(Bucket=bucket, Key=key)["ContentLength"]
|
||||||
|
except botocore.exceptions.ClientError:
|
||||||
|
# Ignore errors - they will be reported later
|
||||||
|
key_sizes[key] = 0
|
||||||
|
|
||||||
if (not keys) or patterns:
|
if (not keys) or patterns:
|
||||||
# Fetch all keys, then filter them if --pattern
|
# Fetch all keys, then filter them if --pattern
|
||||||
all_keys = [
|
all_key_infos = list(paginate(s3, "list_objects_v2", "Contents", Bucket=bucket))
|
||||||
obj["Key"]
|
|
||||||
for obj in paginate(s3, "list_objects_v2", "Contents", Bucket=bucket)
|
|
||||||
]
|
|
||||||
if patterns:
|
if patterns:
|
||||||
filtered = []
|
filtered = []
|
||||||
for pattern in patterns:
|
for pattern in patterns:
|
||||||
filtered.extend(fnmatch.filter(all_keys, pattern))
|
filtered.extend(
|
||||||
|
fnmatch.filter((k["Key"] for k in all_key_infos), pattern)
|
||||||
|
)
|
||||||
keys_to_download.extend(filtered)
|
keys_to_download.extend(filtered)
|
||||||
else:
|
else:
|
||||||
keys_to_download.extend(all_keys)
|
keys_to_download.extend(k["Key"] for k in all_key_infos)
|
||||||
|
if not silent:
|
||||||
|
key_set = set(keys_to_download)
|
||||||
|
for key in all_key_infos:
|
||||||
|
if key["Key"] in key_set:
|
||||||
|
key_sizes[key["Key"]] = key["Size"]
|
||||||
|
|
||||||
output_dir = pathlib.Path(output or ".")
|
output_dir = pathlib.Path(output or ".")
|
||||||
if not output_dir.exists():
|
if not output_dir.exists():
|
||||||
output_dir.mkdir(parents=True)
|
output_dir.mkdir(parents=True)
|
||||||
|
|
||||||
errors = []
|
errors = []
|
||||||
for key in keys_to_download:
|
|
||||||
|
def download(key, callback=None):
|
||||||
# Ensure directory for key exists
|
# Ensure directory for key exists
|
||||||
key_dir = (output_dir / key).parent
|
key_dir = (output_dir / key).parent
|
||||||
if not key_dir.exists():
|
if not key_dir.exists():
|
||||||
key_dir.mkdir(parents=True)
|
key_dir.mkdir(parents=True)
|
||||||
try:
|
try:
|
||||||
s3.download_file(bucket, key, str(output_dir / key))
|
s3.download_file(bucket, key, str(output_dir / key), Callback=callback)
|
||||||
except botocore.exceptions.ClientError as e:
|
except botocore.exceptions.ClientError as e:
|
||||||
errors.append("Not found: {}".format(key))
|
errors.append("Not found: {}".format(key))
|
||||||
|
|
||||||
|
if not silent:
|
||||||
|
total_size = sum(key_sizes.values())
|
||||||
|
with click.progressbar(
|
||||||
|
length=total_size,
|
||||||
|
label="Downloading {} ({} file{})".format(
|
||||||
|
format_bytes(total_size),
|
||||||
|
len(key_sizes),
|
||||||
|
"s" if len(key_sizes) != 1 else "",
|
||||||
|
),
|
||||||
|
) as bar:
|
||||||
|
for key in keys_to_download:
|
||||||
|
download(key, bar.update)
|
||||||
|
else:
|
||||||
|
for key in keys_to_download:
|
||||||
|
download(key)
|
||||||
|
|
||||||
if errors:
|
if errors:
|
||||||
raise click.ClickException("\n".join(errors))
|
raise click.ClickException("\n".join(errors))
|
||||||
|
|
||||||
|
@ -1269,3 +1302,12 @@ def fix_json(row):
|
||||||
for key, value in row.items()
|
for key, value in row.items()
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def format_bytes(size):
|
||||||
|
for x in ("bytes", "KB", "MB", "GB", "TB"):
|
||||||
|
if size < 1024:
|
||||||
|
return "{:3.1f} {}".format(size, x)
|
||||||
|
size /= 1024
|
||||||
|
|
||||||
|
return size
|
||||||
|
|
Ładowanie…
Reference in New Issue