All Products
Search
Document Center

Object Storage Service:Unduhan sederhana (Python SDK V2)

Last Updated:Aug 26, 2026

Topik ini menjelaskan cara menggunakan metode unduhan sederhana untuk mengunduh objek dari bucket ke file lokal. Metode ini bersifat langsung dan ideal untuk mengunduh file dari cloud ke perangkat lokal secara cepat.

Catatan penggunaan

Kode contoh dalam topik ini menggunakan cn-hangzhou, ID wilayah China (Hangzhou), sebagai contoh. Secara default, digunakan endpoint publik. Jika Anda ingin mengakses OSS dari produk Alibaba Cloud lainnya di wilayah yang sama, gunakan endpoint internal. Untuk informasi selengkapnya mengenai wilayah dan endpoint yang didukung oleh OSS, lihat Wilayah dan endpoint.

Izin

Secara default, Akun Alibaba Cloud memiliki izin penuh. Pengguna RAM atau Peran RAM di bawah Akun Alibaba Cloud tidak memiliki izin apa pun secara default. Akun Alibaba Cloud atau administrator akun harus memberikan izin operasi melalui kebijakan RAM atau Kebijakan Bucket.

API

Action

Deskripsi

GetObject

oss:GetObject

Mengunduh objek.

oss:GetObjectVersion

Saat mengunduh objek, jika Anda menentukan versi objek melalui versionId, izin ini diperlukan.

kms:Decrypt

Saat mengunduh objek, jika metadata objek berisi X-Oss-Server-Side-Encryption: KMS, izin ini diperlukan.

Definisi metode

get_object(request: GetObjectRequest, **kwargs) → GetObjectResult

Parameter permintaan

Parameter

Tipe

Deskripsi

request

GetObjectRequest

Parameter permintaan. Untuk informasi selengkapnya, lihat GetObjectRequest

Nilai kembali

Tipe

Deskripsi

GetObjectResult

Nilai kembali. Untuk informasi selengkapnya, lihat GetObjectResult

Untuk definisi lengkap metode unduhan sederhana, lihat get_object.

Kode contoh

Anda dapat menggunakan kode berikut untuk mengunduh objek dari bucket ke file lokal.

import argparse
import alibabacloud_oss_v2 as oss
import os

# Buat parser argumen command-line.
parser = argparse.ArgumentParser(description="contoh get object")

# Tambahkan argumen command-line --region untuk menentukan wilayah tempat bucket berada. Argumen ini wajib.
parser.add_argument('--region', help='Wilayah tempat bucket berada.', required=True)
# Tambahkan argumen command-line --bucket untuk menentukan nama bucket. Argumen ini wajib.
parser.add_argument('--bucket', help='Nama bucket.', required=True)
# Tambahkan argumen command-line --endpoint untuk menentukan nama domain yang dapat digunakan layanan lain untuk mengakses OSS. Argumen ini opsional.
parser.add_argument('--endpoint', help='Nama domain yang dapat digunakan layanan lain untuk mengakses OSS')
# Tambahkan argumen command-line --key untuk menentukan nama objek. Argumen ini wajib.
parser.add_argument('--key', help='Nama objek.', required=True)

def main():
    # Parsing argumen command-line.
    args = parser.parse_args()

    # Muat kredensial dari variabel lingkungan untuk verifikasi identitas.
    credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()

    # Muat konfigurasi default SDK dan atur penyedia kredensial.
    cfg = oss.config.load_default()
    cfg.credentials_provider = credentials_provider

    # Atur wilayah dalam konfigurasi.
    cfg.region = args.region

    # Jika endpoint disediakan, atur endpoint dalam konfigurasi.
    if args.endpoint is not None:
        cfg.endpoint = args.endpoint

    # Gunakan informasi yang telah dikonfigurasi untuk membuat client OSS.
    client = oss.Client(cfg)

    # Eksekusi permintaan untuk mendapatkan objek. Tentukan nama bucket dan nama objek.
    result = client.get_object(oss.GetObjectRequest(
        bucket=args.bucket,  # Tentukan nama bucket.
        key=args.key,  # Tentukan kunci objek.
    ))

    # Cetak hasil mendapatkan objek untuk memeriksa apakah permintaan berhasil.
    print(f'kode status: {result.status_code},'
          f' ID permintaan: {result.request_id},'
          f' panjang konten: {result.content_length},'
          f' rentang konten: {result.content_range},'
          f' tipe konten: {result.content_type},'
          f' etag: {result.etag},'
          f' terakhir dimodifikasi: {result.last_modified},'
          f' md5 konten: {result.content_md5},'
          f' kontrol cache: {result.cache_control},'
          f' disposisi konten: {result.content_disposition},'
          f' encoding konten: {result.content_encoding},'
          f' kedaluwarsa: {result.expires},'
          f' hash crc64: {result.hash_crc64},'
          f' kelas penyimpanan: {result.storage_class},'
          f' tipe objek: {result.object_type},'
          f' ID versi: {result.version_id},'
          f' jumlah tagging: {result.tagging_count},'
          f' enkripsi sisi server: {result.server_side_encryption},'
          f' enkripsi data sisi server: {result.server_side_data_encryption},'
          f' posisi append berikutnya: {result.next_append_position},'
          f' kedaluwarsa: {result.expiration},'
          f' pemulihan: {result.restore},'
          f' status proses: {result.process_status},'
          f' penanda hapus: {result.delete_marker},'
    )

    # ========== Metode 1: Baca seluruh objek ==========
    with result.body as body_stream:
        data = body_stream.read()
        print(f"File telah dibaca. Panjang data: {len(data)} byte")

        path = "./get-object-sample.txt"
        with open(path, 'wb') as f:
            f.write(data)
        print(f"File telah diunduh dan disimpan ke path: {path}")

    # # ========== Metode 2: Baca dalam potongan ==========
    # with result.body as body_stream:
    #     chunk_path = "./get-object-sample-chunks.txt"
    #     total_size = 0

    #     with open(chunk_path, 'wb') as f:
    #         # Gunakan ukuran blok 256 KB. Anda dapat mengubah parameter block_size sesuai kebutuhan.
    #         for chunk in body_stream.iter_bytes(block_size=256 * 1024):
    #             f.write(chunk)
    #             total_size += len(chunk)
    #             print(f"Blok data diterima: {len(chunk)} byte | Total: {total_size} byte")

    #     print(f"File telah diunduh dan disimpan ke path: {chunk_path}")

# Saat skrip ini dijalankan secara langsung, fungsi main dipanggil.
if __name__ == "__main__":
    main()  # Titik masuk skrip. Saat file dijalankan secara langsung, fungsi main dipanggil.

Skenario

Unduhan bersyarat

Saat mengunduh objek dari bucket, Anda dapat menentukan kondisi berdasarkan waktu modifikasi terakhir atau ETag objek tersebut. Objek hanya akan diunduh jika kondisi tersebut terpenuhi; jika tidak, error akan dikembalikan dan operasi unduhan tidak dilanjutkan. Pendekatan ini mengurangi transmisi jaringan dan konsumsi sumber daya yang tidak perlu, sekaligus meningkatkan efisiensi unduhan.

Tabel berikut menjelaskan kondisi yang tersedia.

Catatan
  • Parameter if_modified_since dan if_unmodified_since dapat digunakan bersamaan. Demikian pula, if_match dan if_none_match dapat digunakan secara bersamaan.

  • Anda dapat menggunakan metode client.get_object_meta untuk mendapatkan ETag.

Parameter

Deskripsi

if_modified_since

Jika waktu yang ditentukan lebih awal daripada waktu objek terakhir dimodifikasi, objek dapat diunduh. Jika tidak, respons 304 Not modified dikembalikan.

if_unmodified_since

Jika waktu yang ditentukan lebih lambat atau sama dengan waktu objek terakhir dimodifikasi, objek dapat diunduh. Jika tidak, respons 412 Precondition failed dikembalikan.

if_match

Jika ETag yang ditentukan cocok dengan ETag objek, objek dapat diunduh. Jika tidak, respons 412 Precondition failed dikembalikan.

if_none_match

Jika ETag yang ditentukan tidak cocok dengan ETag objek, objek dapat diunduh. Jika tidak, respons 304 Not modified dikembalikan.

Kode contoh berikut menunjukkan cara menggunakan unduhan bersyarat.

import argparse
import alibabacloud_oss_v2 as oss
from datetime import datetime, timezone

# Buat parser argumen command-line dan jelaskan tujuan skrip: contoh get object dan simpan ke file.
parser = argparse.ArgumentParser(description="contoh get object ke file")

# Tambahkan argumen command-line --region untuk menentukan wilayah tempat bucket berada. Argumen ini wajib.
parser.add_argument('--region', help='Wilayah tempat bucket berada.', required=True)
# Tambahkan argumen command-line --bucket untuk menentukan nama bucket tempat objek diambil. Argumen ini wajib.
parser.add_argument('--bucket', help='Nama bucket.', required=True)
# Tambahkan argumen command-line --endpoint untuk menentukan nama domain yang dapat digunakan layanan lain untuk mengakses OSS. Argumen ini opsional.
parser.add_argument('--endpoint', help='Nama domain yang dapat digunakan layanan lain untuk mengakses OSS')
# Tambahkan argumen command-line --key untuk menentukan kunci objek (file) di OSS. Argumen ini wajib.
parser.add_argument('--key', help='Nama objek.', required=True)
# Tambahkan argumen command-line --file_path untuk menentukan path lokal file yang diunduh. Argumen ini wajib.
parser.add_argument('--file_path', help='Path file untuk menyimpan konten yang diunduh.', required=True)

def main():
    # Parsing argumen command-line untuk mendapatkan nilai input pengguna.
    args = parser.parse_args()

    # Muat kredensial yang diperlukan untuk mengakses OSS dari variabel lingkungan untuk verifikasi identitas.
    credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()

    # Gunakan konfigurasi default SDK untuk membuat objek konfigurasi dan atur penyedia kredensial.
    cfg = oss.config.load_default()
    cfg.credentials_provider = credentials_provider
    
    # Atur properti region pada objek konfigurasi berdasarkan argumen command-line.
    cfg.region = args.region

    # Jika endpoint kustom disediakan, perbarui properti endpoint pada objek konfigurasi.
    if args.endpoint is not None:
        cfg.endpoint = args.endpoint

    # Gunakan konfigurasi sebelumnya untuk menginisialisasi client OSS guna berinteraksi dengan OSS.
    client = oss.Client(cfg)

    # Definisikan waktu if_modified_since.
    # Hanya objek yang dimodifikasi setelah waktu ini yang dikembalikan.
    if_modified_since = datetime(2024, 10, 1, 12, 0, 0, tzinfo=timezone.utc)

    # Asumsikan ETag adalah DA5223EFCD7E0353BE08866700000000. Jika ETag yang ditentukan sama dengan ETag objek, kondisi IfMatch terpenuhi dan unduhan dijalankan.
    etag = "\"DA5223EFCD7E0353BE08866700000000\""

    # Eksekusi permintaan untuk mendapatkan objek dan simpan ke file lokal.
    result = client.get_object_to_file(
        oss.GetObjectRequest(
            bucket=args.bucket,  # Tentukan nama bucket.
            key=args.key,        # Tentukan kunci objek.
            if_modified_since=if_modified_since,  # Hanya objek yang dimodifikasi setelah waktu tertentu yang dikembalikan.
            if_match=etag,       # Hanya objek dengan ETag yang cocok yang dikembalikan.
        ),
        args.file_path  # Tentukan path lokal tempat file diunduh.
    )

    # Cetak hasil mendapatkan objek, termasuk kode status dan ID permintaan.
    print(f'kode status: {result.status_code},'
          f' ID permintaan: {result.request_id},'
          f' panjang konten: {result.content_length},'
          f' rentang konten: {result.content_range},'
          f' tipe konten: {result.content_type},'
          f' etag: {result.etag},'
          f' terakhir dimodifikasi: {result.last_modified},'
          f' md5 konten: {result.content_md5},'
          f' kontrol cache: {result.cache_control},'
          f' disposisi konten: {result.content_disposition},'
          f' encoding konten: {result.content_encoding},'
          f' kedaluwarsa: {result.expires},'
          f' hash crc64: {result.hash_crc64},'
          f' kelas penyimpanan: {result.storage_class},'
          f' tipe objek: {result.object_type},'
          f' ID versi: {result.version_id},'
          f' jumlah tagging: {result.tagging_count},'
          f' enkripsi sisi server: {result.server_side_encryption},'
          f' enkripsi data sisi server: {result.server_side_data_encryption},'
          f' posisi append berikutnya: {result.next_append_position},'
          f' kedaluwarsa: {result.expiration},'
          f' pemulihan: {result.restore},'
          f' status proses: {result.process_status},'
          f' penanda hapus: {result.delete_marker},'
          f' waktu server: {result.headers.get("x-oss-server-time")},'
    )

# Saat skrip ini dijalankan secara langsung, panggil fungsi main untuk memulai logika pemrosesan.
if __name__ == "__main__":
    main()  # Titik masuk skrip. Alur program dimulai dari sini.

Tampilkan progress bar untuk unduhan file

Saat mengunduh file, Anda dapat menggunakan progress bar untuk memantau kemajuan unduhan secara real time. Fitur ini membantu melacak status unduhan dan memastikan tugas berjalan sesuai harapan, terutama berguna untuk unduhan berdurasi panjang.

Kode contoh berikut menunjukkan cara menampilkan progress bar saat mengunduh objek ke file lokal, menggunakan metode get_object_to_file sebagai contoh.

import argparse
import alibabacloud_oss_v2 as oss

# Buat parser argumen command-line dan jelaskan tujuan skrip: contoh get object.
parser = argparse.ArgumentParser(description="contoh get object")

# Tambahkan argumen command-line --region untuk menentukan wilayah tempat bucket berada. Argumen ini wajib.
parser.add_argument('--region', help='Wilayah tempat bucket berada.', required=True)
# Tambahkan argumen command-line --bucket untuk menentukan nama bucket tempat objek diambil. Argumen ini wajib.
parser.add_argument('--bucket', help='Nama bucket.', required=True)
# Tambahkan argumen command-line --endpoint untuk menentukan nama domain yang dapat digunakan layanan lain untuk mengakses OSS. Argumen ini opsional.
parser.add_argument('--endpoint', help='Nama domain yang dapat digunakan layanan lain untuk mengakses OSS')
# Tambahkan argumen command-line --key untuk menentukan kunci objek (file) di OSS. Argumen ini wajib.
parser.add_argument('--key', help='Nama objek.', required=True)

def main():
    # Parsing argumen command-line untuk mendapatkan nilai input pengguna.
    args = parser.parse_args()

    # Muat kredensial yang diperlukan untuk mengakses OSS dari variabel lingkungan untuk verifikasi identitas.
    credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()

    # Gunakan konfigurasi default SDK untuk membuat objek konfigurasi dan atur penyedia kredensial.
    cfg = oss.config.load_default()
    cfg.credentials_provider = credentials_provider

    # Atur properti region pada objek konfigurasi berdasarkan argumen command-line.
    cfg.region = args.region

    # Jika endpoint kustom disediakan, perbarui properti endpoint pada objek konfigurasi.
    if args.endpoint is not None:
        cfg.endpoint = args.endpoint

    # Gunakan konfigurasi sebelumnya untuk menginisialisasi client OSS guna berinteraksi dengan OSS.
    client = oss.Client(cfg)

    # Definisikan variabel dictionary progress_state untuk menyimpan status kemajuan unduhan. Nilai awal adalah 0.
    progress_state = {'saved': 0}
    
    # Definisikan fungsi callback progress _progress_fn.
    def _progress_fn(n, written, total):
        # Gunakan dictionary untuk menyimpan jumlah byte yang telah ditulis secara kumulatif.
        progress_state['saved'] += n

        # Hitung persentase unduhan saat ini. Bagi jumlah byte yang telah ditulis dengan jumlah total byte dan bulatkan ke bawah menjadi bilangan bulat.
        rate = int(100 * (float(written) / float(total)))

        # Cetak kemajuan unduhan saat ini. \r berarti kembali ke awal baris untuk menerapkan refresh real-time di command line.
        # end='' berarti tanpa line break, sehingga cetakan berikutnya menimpa baris saat ini.
        print(f'\r{rate}% ', end='')

    # Eksekusi permintaan untuk mendapatkan objek. Tentukan nama bucket, nama objek, dan fungsi callback progress.
    result = client.get_object_to_file(
        oss.GetObjectRequest(
            bucket=args.bucket,  # Tentukan nama bucket.
            key=args.key,        # Tentukan kunci objek.
            progress_fn=_progress_fn, # Tentukan fungsi callback progress.
        ),
        "/local/dir/example", # Tentukan path lokal tempat file disimpan.
    )

    # Cetak hasil mendapatkan objek.
    print(vars(result))

# Saat skrip ini dijalankan secara langsung, panggil fungsi main untuk memulai logika pemrosesan.
if __name__ == "__main__":
    main()  # Titik masuk skrip. Alur program dimulai dari sini.

Kode contoh berikut menunjukkan cara menampilkan progress bar untuk unduhan streaming, menggunakan metode get_object sebagai contoh.

import argparse
import alibabacloud_oss_v2 as oss
import os

# Buat parser argumen command-line.
parser = argparse.ArgumentParser(description="contoh get object")

# Tambahkan argumen command-line --region untuk menentukan wilayah tempat bucket berada. Argumen ini wajib.
parser.add_argument('--region', help='Wilayah tempat bucket berada.', required=True)
# Tambahkan argumen command-line --bucket untuk menentukan nama bucket. Argumen ini wajib.
parser.add_argument('--bucket', help='Nama bucket.', required=True)
# Tambahkan argumen command-line --endpoint untuk menentukan nama domain yang dapat digunakan layanan lain untuk mengakses OSS. Argumen ini opsional.
parser.add_argument('--endpoint', help='Nama domain yang dapat digunakan layanan lain untuk mengakses OSS')
# Tambahkan argumen command-line --key untuk menentukan nama objek. Argumen ini wajib.
parser.add_argument('--key', help='Nama objek.', required=True)

def main():
    # Parsing argumen command-line.
    args = parser.parse_args()

    # Muat kredensial yang diperlukan untuk mengakses OSS dari variabel lingkungan untuk verifikasi identitas.
    credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()

    # Muat konfigurasi default SDK dan atur penyedia kredensial.
    cfg = oss.config.load_default()
    cfg.credentials_provider = credentials_provider

    # Atur wilayah dalam konfigurasi.
    cfg.region = args.region

    # Jika endpoint disediakan, atur endpoint dalam konfigurasi.
    if args.endpoint is not None:
        cfg.endpoint = args.endpoint

    # Gunakan informasi yang telah dikonfigurasi untuk membuat client OSS.
    client = oss.Client(cfg)

    # Eksekusi permintaan untuk mendapatkan objek. Tentukan nama bucket dan nama objek.
    result = client.get_object(oss.GetObjectRequest(
        bucket=args.bucket,  # Tentukan nama bucket.
        key=args.key,  # Tentukan kunci objek.
    ))

    # Hasil yang dikembalikan untuk mendapatkan objek berisi ukuran total file dalam byte.
    total_size = result.content_length

    # Inisialisasi penghitung progres ke 0 untuk mencatat jumlah data yang telah diunduh.
    progress_save_n = 0

    # Iterasi blok data dalam badan respons untuk membaca data per blok.
    for d in result.body.iter_bytes():
        # Tambahkan panjang blok data saat ini ke jumlah total yang telah diunduh.
        progress_save_n += len(d)

        # Hitung persentase unduhan saat ini. Konversi rasio jumlah yang telah diunduh terhadap ukuran total menjadi persentase dan bulatkan ke bawah menjadi bilangan bulat.
        rate = int(100 * (float(progress_save_n) / float(total_size)))

        # Cetak kemajuan unduhan saat ini. \r berarti kembali ke awal baris untuk menerapkan refresh real-time di command line.
        # end='' berarti tanpa line break, sehingga cetakan berikutnya menimpa baris saat ini.
        print(f'\r{rate}% ', end='')

    # Cetak semua informasi properti objek hasil untuk debugging atau melihat konten respons lengkap.
    print(vars(result))


# Saat skrip ini dijalankan secara langsung, fungsi main dipanggil.
if __name__ == "__main__":
    main()  # Titik masuk skrip. Saat file dijalankan secara langsung, fungsi main dipanggil.

Unduhan batch file ke perangkat lokal

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import argparse
import alibabacloud_oss_v2 as oss
import os
import sys
import threading
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path
from typing import List, Tuple, Optional
import signal

class DownloadTask:
    """Kelas tugas unduhan"""
    def __init__(self, object_key: str, local_path: str, size: int):
        self.object_key = object_key
        self.local_path = local_path
        self.size = size

class DownloadResult:
    """Kelas hasil unduhan"""
    def __init__(self, object_key: str, success: bool = False, error: Optional[str] = None, size: int = 0):
        self.object_key = object_key
        self.success = success
        self.error = error
        self.size = size

class BatchDownloader:
    """Pengunduh batch"""

    def __init__(self, client: oss.Client, bucket: str, max_workers: int = 5):
        self.client = client
        self.bucket = bucket
        self.max_workers = max_workers
        self.stop_event = threading.Event()

    def list_objects(self, prefix: str = "", max_keys: int = 1000) -> List[DownloadTask]:
        """Daftar semua objek yang memiliki awalan tertentu di bucket."""
        tasks = []
        continuation_token = None

        print(f"Memindai file di bucket...")

        while not self.stop_event.is_set():
            try:
                # Buat permintaan untuk mendaftar objek.
                request = oss.ListObjectsV2Request(
                    bucket=self.bucket,
                    prefix=prefix,
                    max_keys=max_keys,
                    continuation_token=continuation_token
                )

                # Eksekusi operasi daftar.
                result = self.client.list_objects_v2(request)

                # Proses hasil daftar.
                for obj in result.contents:
                    # Lewati objek folder, yang diakhiri dengan garis miring (/) dan memiliki ukuran 0.
                    if obj.key.endswith('/') and obj.size == 0:
                        continue

                    # Hitung path file lokal.
                    relative_path = obj.key[len(prefix):] if prefix else obj.key

                    tasks.append(DownloadTask(
                        object_key=obj.key,
                        local_path=relative_path,
                        size=obj.size
                    ))

                # Periksa apakah masih ada objek lainnya.
                if not result.next_continuation_token:
                    break
                continuation_token = result.next_continuation_token

            except Exception as e:
                raise Exception(f"Gagal mendaftar objek: {str(e)}")

        return tasks

    def download_file(self, task: DownloadTask, local_dir: str) -> DownloadResult:
        """Unduh satu file."""
        result = DownloadResult(task.object_key, size=task.size)

        try:
            # Hitung path file lokal lengkap.
            full_local_path = os.path.join(local_dir, task.local_path)

            # Buat direktori file lokal.
            os.makedirs(os.path.dirname(full_local_path), exist_ok=True)

            # Periksa apakah file sudah ada dan memiliki ukuran yang sama untuk unduhan yang dapat dilanjutkan.
            if os.path.exists(full_local_path):
                local_size = os.path.getsize(full_local_path)
                if local_size == task.size:
                    result.success = True
                    return result

            # Buat permintaan unduhan.
            get_request = oss.GetObjectRequest(
                bucket=self.bucket,
                key=task.object_key
            )

            # Eksekusi unduhan.
            response = self.client.get_object(get_request)

            # Simpan file.
            with open(full_local_path, 'wb') as f:
                with response.body as body_stream:
                    # Baca dan tulis dalam potongan.
                    for chunk in body_stream.iter_bytes(block_size=1024 * 1024):  # Potongan 1 MB
                        if self.stop_event.is_set():
                            raise Exception("Unduhan terganggu")
                        f.write(chunk)

            result.success = True

        except Exception as e:
            result.error = str(e)
            # Jika unduhan gagal, hapus file yang belum lengkap.
            try:
                if os.path.exists(full_local_path):
                    os.remove(full_local_path)
            except:
                pass

        return result

    def batch_download(self, tasks: List[DownloadTask], local_dir: str) -> List[DownloadResult]:
        """Eksekusi unduhan batch."""
        results = []
        completed = 0
        total = len(tasks)

        print(f"Mulai mengunduh {total} file menggunakan {self.max_workers} thread konkuren...")

        with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
            # Kirim semua tugas unduhan.
            future_to_task = {
                executor.submit(self.download_file, task, local_dir): task
                for task in tasks
            }

            # Proses tugas yang selesai.
            for future in as_completed(future_to_task):
                if self.stop_event.is_set():
                    break

                task = future_to_task[future]
                try:
                    result = future.result()
                    results.append(result)
                    completed += 1

                    # Tampilkan progres.
                    if result.success:
                        print(f"✓ [{completed}/{total}] {result.object_key} ({self.format_bytes(result.size)})")
                    else:
                        print(f"✗ [{completed}/{total}] {result.object_key} - Error: {result.error}")

                except Exception as e:
                    result = DownloadResult(task.object_key, error=str(e), size=task.size)
                    results.append(result)
                    completed += 1
                    print(f"✗ [{completed}/{total}]

Contoh penggunaan

# Unduh semua file dengan awalan images/2024/ dari bucket my-bucket.
python batch_download.py --region cn-hangzhou --bucket my-bucket --prefix images/2024/

# Unduh file ke direktori lokal tertentu.
python batch_download.py --region cn-hangzhou --bucket my-bucket --prefix documents/ --local-dir ./my-downloads

# Gunakan lebih banyak worker konkuren untuk unduhan.
python batch_download.py --region cn-hangzhou --bucket my-bucket --prefix videos/ --workers 10

# Unduh semua file di bucket dengan menghilangkan parameter prefix atau menentukan string kosong sebagai prefix.
python batch_download.py --region cn-hangzhou --bucket my-bucket

# Atau, tentukan secara eksplisit prefix kosong.
python batch_download.py --region cn-hangzhou --bucket my-bucket --prefix ""

Contoh output

Program menampilkan progres unduhan detail saat runtime:

Memulai unduhan batch
Bucket: my-bucket
Prefix: 'images/2024/'
Direktori lokal: ./downloads
Konkurensi: 5
--------------------------------------------------
Memindai file di bucket...
Ditemukan 150 file untuk diunduh
--------------------------------------------------
Mulai mengunduh 150 file menggunakan 5 thread konkuren...
✓ [1/150] images/2024/photo1.jpg (2,3 MB)
✓ [2/150] images/2024/photo2.png (1,8 MB)
✗ [3/150] images/2024/photo3.gif - Error: Request timeout
✓ [4/150] images/2024/subfolder/photo4.jpg (3,1 MB)
...
✓ [150/150] images/2024/thumbnails/thumb150.jpg (256,0 KB)
--------------------------------------------------
Unduhan selesai!
Berhasil: 148
Gagal: 2
Ukuran total: 1,2 GB
Durasi: 45,67 detik

File gagal:
  - images/2024/photo3.gif: Request timeout
  - images/2024/corrupted.jpg: Invalid response

Referensi