Skip to main content
  • »
  • S3 »
  • Jak uzyskać dostęp do object storage z NSIS za pomocą boto3

Jak uzyskać dostęp do object storage z NSIS za pomocą boto3

W tym artykule dowiesz się, jak uzyskać dostęp do object storage z NSIS przy użyciu biblioteki Python boto3.

Wymagania wstępne

Nr 1 Konto

Potrzebne jest konto hostingowe NSIS z dostępem do interfejsu Horizon: https://horizon.cloudferro.com.

Nr 2 Generowane poświadczenia EC2

Musisz wygenerować poświadczenia EC2. Dowiedz się więcej tutaj: Jak wygenerować poświadczenia EC2 i zarządzać nimi na NSIS Cloud

Nr 3 Komputer z systemem Linux lub Windows lub maszyna wirtualna

Nr 3 Komputer z systemem Linux lub maszyna wirtualna

Ten artykuł został napisany dla Ubuntu 22.04. Inne systemy operacyjne mogą działać, ale są poza zakresem tego artykułu i mogą wymagać dostosowania dostarczonych tutaj poleceń.

Z tego artykułu można korzystać zarówno na maszynach wirtualnych z systemem Linux lub Windows, jak i na komputerze lokalnym z jednym z tych systemów operacyjnych.

Aby zbudować nową maszynę wirtualną hostowaną w chmurze NSIS, pomocny może być jeden z poniższych artykułów:

Nr 4 Dodatkowe biblioteki Pythona

Będziemy używać dwóch bibliotek Pythona do wypychania plików na serwer S3 w NSIS:

requests

Powszechnie używany do dostępu HTTP.

requests_aws4auth

Biblioteka potrzebna do podpisywania przesyłanych plików na serwer S3. Zainstaluj je za pomocą

pip install requests
pip install requests-aws4auth

i import za pomocą poleceń Pythona

import boto3
import requests
from requests_aws4auth import AWS4Auth

Terminologia: kontener i bucket

Terminy kontener (używany przez pulpit nawigacyjny Horizon) i bucket (używany przez boto3) są tutaj używane zamiennie, aby opisać to samo.

Przygotowanie środowiska

Aby wykonać przykłady przedstawione w tym artykule, będziesz potrzebować działającej instalacji Pythona 3 i biblioteki boto3. Dokładna metoda instalacji będzie zależeć od twoich celów i nawyków pracy z Pythonem.

Ubuntu 22.04

Metoda 1: Korzystanie z virtualenvwrapper i pip

Jeśli jesteś doświadczonym użytkownikiem Pythona i pracujesz nad wieloma projektami, z pewnością korzystasz z wirtualnych środowisk Pythona. Zaleca się utworzenie nowego wirtualnego środowiska dla boto3, a oto jeden ze sposobów, aby to zrobić: Jak zainstalować Python virtualenv lub virtualenvwrapper na NSIS Cloud.

Jak wspomniano w tym artykule, użyj polecenia workon, aby wejść do środowiska wirtualnego. Na przykład, jeśli twoje środowisko nazywa się managing-files wykonaj:

workon managing-files

Po aktywacji środowiska zainstaluj boto3:

pip3 install boto3

Metoda 2: Korzystanie z apt

Jeśli jednak nie potrzebujesz takiego środowiska i chcesz udostępnić boto3 globalnie pod Ubuntu, użyj apt. Poniższe polecenie zainstaluje zarówno Python3 jak i bibliotekę boto3:

sudo apt install python3 python3-boto3

Windows Server 2022

Jeśli korzystasz z systemu Windows, postępuj zgodnie z tym artykułem, aby dowiedzieć się, jak zainstalować boto3: Jak zainstalować Boto3 w Windows na NSIS.

Jak korzystać z podanych przykładów?

Każdy z podanych przykładów może służyć jako samodzielny kod. Powinieneś być w stanie

  • wprowadzić kod w edytorze tekstu,

  • określić odpowiednią zawartość dla dostarczonych zmiennych,

  • zapisać go jako plik i

  • uruchomić go.

Przykłady te można również wykorzystać jako punkt wyjścia dla własnego kodu.

../_images/code_structure1.png

Każdy przykład składa się z trzech części:

niebieski prostokąt Zaimportuj niezbędne biblioteki

Standardową praktyką Pythona jest importowanie wszystkich bibliotek potrzebnych później do kodu. W tym przypadku, najpierw zaimportuj bibliotekę boto3, jak również inne, jeśli takie są.

czerwony prostokąt Definiowanie parametrów wejściowych

Typowe wywołanie w bibliotece boto3 będzie wyglądać następująco:

s3=boto3.resource('s3',
    aws_access_key_id=access_key,
    aws_secret_access_key=secret_key,
    endpoint_url=endpoint_url,
    region_name=region_name)

For each call, several variables must be present:

 * **aws_access_key_id** -- from Prerequisite No. 2
 * **aws_secret_access_key** -- also from Prerequisite No. 2
 * **endpoint_url** -- s3.waw4-1.cloudferro.com
 * **region_name** -- US

Inne zmienne mogą być potrzebne w zależności od przypadku:

  • name,

  • path

i ewentualnie inne. Pamiętaj, aby wprowadzić wartości wszystkich tych dodatkowych zmiennych przed uruchomieniem przykładów.

zielony prostokąt boto3 call

Większość kodu przygotowawczego pozostaje taka sama od pliku do pliku, a ta ostatnia część jest miejscem, w którym mają miejsce różne działania. Czasami będzie to tylko jedna linia kodu, czasami kilka, zwykle pętla.

Uruchamianie kodu Python

Po wprowadzeniu wszystkich wartości do odpowiednich zmiennych, można zapisać plik z rozszerzeniem .py. Jednym ze sposobów uruchamiania skryptów Pythona jest użycie wiersza poleceń. Aby uruchomić taki skrypt, należy najpierw przejść do folderu, w którym znajduje się skrypt. Następnie wykonaj poniższe polecenie, ale zastąp script.py nazwą skryptu, który chcesz uruchomić.

python3 script.py

Upewnij się, że nazwa i/lub lokalizacja pliku jest poprawnie przekazywana do powłoki - uważaj na spacje i inne znaki specjalne.

Ważne

W tych przykładach, nawet w przypadku potencjalnie destrukcyjnych operacji, nie zostaniesz poproszony o potwierdzenie. Jeśli na przykład skrypt zapisuje plik, a plik już istnieje pod wskazaną nazwą i lokalizacją, prawdopodobnie zostanie nadpisany. Przed uruchomieniem skryptu upewnij się, że tego właśnie chcesz, lub rozszerz kod o sprawdzanie, czy plik już istnieje.

We wszystkich poniższych przykładach zakładamy, że odpowiednie pliki i buckety już istnieją. Dodawanie kodu do sprawdzania tego rodzaju jest poza zakresem tego artykułu.

Tworzenie kontenera

Aby utworzyć nowy kontener, najpierw wybierz nazwę dla swojego bucketu i wprowadź ją w zmiennej name. W chmurze NSIS należy używać tylko liter, cyfr i myślników.

boto3_create_bucket.py

import boto3

access_key = ''
secret_key = ''

name = 'boto3'
endpoint_url='https://s3.waw4-1.cloudferro.com'
region_name='US'

try:

    s3=boto3.resource('s3',
        aws_access_key_id=access_key,
        aws_secret_access_key=secret_key,
        endpoint_url=endpoint_url,
        region_name=region_name)

    s3.Bucket(name).create()

except Exception as issue:
    print("The following error occurred:")
    print(issue)

Pomyślne wykonanie tego kodu nie powinno dać żadnych wyników.

Aby sprawdzić, czy bucket został utworzony, można między innymi wyświetlić listę bucketów zgodnie z opisem w sekcji Wyświetlanie bucketów poniżej.

Rozwiązywanie problemów z tworzeniem kontenera

Bucket już istnieje

Jeśli otrzymasz następujące dane wyjściowe:

The following error occurred:
An error occurred (BucketAlreadyExists) when calling the CreateBucket operation: Unknown

oznacza to, że nie można wybrać tej nazwy dla bucketu, ponieważ bucket o tej nazwie już istnieje. Wybierz inną nazwę i spróbuj ponownie.

Użyto nieprawidłowych znaków

Jeśli w nazwie kontenera użyto nieprawidłowych znaków, powinien pojawić się błąd podobny do tego:

Invalid bucket name "this container should not exist": Bucket name must match the regex "^[a-zA-Z0-9.\-_]{1,255}$" or be an ARN matching the regex "^arn:(aws).*:(s3|s3-object-lambda):[a-z\-0-9]*:[0-9]{12}:accesspoint[/:][a-zA-Z0-9\-.]{1,63}$|^arn:(aws).*:s3-outposts:[a-z\-0-9]+:[0-9]{12}:outpost[/:][a-zA-Z0-9\-]{1,63}[/:]accesspoint[/:][a-zA-Z0-9\-]{1,63}$"

Aby rozwiązać problem, wybierz inną nazwę - w chmurze NSIS używaj tylko liter, cyfr i myślników.

Wylistowanie bucketów

Ten kod umożliwia wyświetlenie listy bucketów.

boto3_list_buckets.py

import boto3

access_key = ''
secret_key = ''

name = 'boto3'
endpoint_url='https://s3.waw4-1.cloudferro.com'
region_name='US'

try:

    s3=boto3.client('s3',
        aws_access_key_id=access_key,
        aws_secret_access_key=secret_key,
        endpoint_url=endpoint_url,
        region_name=region_name)

    print(s3.list_buckets()['Buckets'])

except Exception as issue:
    print("The following error occurred:")
    print(issue)

Wynikiem powinna być lista słowników, z których każdy zawiera informacje dotyczące określonego bucketu, zaczynając od jego nazwy. Jeśli istnieją już dwa buckety, moje-pliki i moje-inne-pliki, wynik może być podobny do tego:

[{'Name': 'boto3', 'CreationDate': datetime.datetime(2025, 4, 30, 8, 52, 30, 658000, tzinfo=tzutc())}, {'Name': 'mapsrv', 'CreationDate': datetime.datetime(2025, 1, 7, 12, 43, 50, 522000, tzinfo=tzutc())}, {'Name': 'mapsrv-rasters', 'CreationDate': datetime.datetime(2025, 1, 7, 12, 44, 23, 39000, tzinfo=tzutc())}, {'Name': 's2ffb', 'CreationDate': datetime.datetime(2025, 1, 7, 13, 1, 37, 806000, tzinfo=tzutc())}, {'Name': 's2ffb82', 'CreationDate': datetime.datetime(2025, 1, 7, 13, 1, 45, 124000, tzinfo=tzutc())}, {'Name': 's2landkreise', 'CreationDate': datetime.datetime(2025, 1, 7, 13, 2, 6, 887000, tzinfo=tzutc())}, {'Name': 's2lkffb', 'CreationDate': datetime.datetime(2025, 1, 7, 13, 2, 20, 140000, tzinfo=tzutc())}, {'Name': 'yy-1', 'CreationDate': datetime.datetime(2025, 2, 20, 15, 38, 40, 454000, tzinfo=tzutc())}]

Aby uprościć dane wyjściowe, użyj pętli for z instrukcją print, aby wyświetlić tylko nazwy bucketów, po jednym bucket w wierszu:

boto3_list_one_by_one.py

import boto3

access_key = ''
secret_key = ''

name = 'boto3'
endpoint_url='https://s3.waw4-1.cloudferro.com'
region_name='US'

try:

    s3=boto3.client('s3',
        aws_access_key_id=access_key,
        aws_secret_access_key=secret_key,
        endpoint_url=endpoint_url,
        region_name=region_name)

    for i in s3.list_buckets()['Buckets']:
        print(i['Name'])

except Exception as issue:
    print("The following error occurred:")
    print(issue)

Przykładowy wynik dla tego kodu może wyglądać następująco:

boto3
mapsrv
mapsrv-rasters

Jeśli nie masz żadnych bucketów, dane wyjściowe powinny być puste.

Sprawdzanie, kiedy bucket został utworzony

Użyj tego kodu, aby sprawdzić datę utworzenia bucketu. Wprowadź nazwę tego bucketu w zmiennej name.

boto3_check_date.py

import boto3

access_key = ''
secret_key = ''

name = 'boto3'
endpoint_url='https://s3.waw4-1.cloudferro.com'
region_name='US'

try:

    s3=boto3.resource('s3',
        aws_access_key_id=access_key,
        aws_secret_access_key=secret_key,
        endpoint_url=endpoint_url,
        region_name=region_name)

    print(s3.Bucket(name).creation_date)

except Exception as issue:
    print("The following error occurred:")
    print(issue)

Dane wyjściowe powinny zawierać datę utworzenia bucketu w formacie obiektu Python datetime:

2024-01-23 14:21:03.070000+00:00

Wyświetlanie plików w bucket

Aby wyświetlić listę plików znajdujących się w bucket, należy podać nazwę bucketu w zmiennej name.

boto3_list_files_in_bucket.py

import boto3

access_key = ''
secret_key = ''

name = 'boto3'
endpoint_url='https://s3.waw4-1.cloudferro.com'
region_name='US'

try:

    s3=boto3.resource('s3',
        aws_access_key_id=access_key,
        aws_secret_access_key=secret_key,
        endpoint_url=endpoint_url,
        region_name=region_name)

    bucket=s3.Bucket(name)
    for obj in bucket.objects.filter():
       print(obj)

except Exception as issue:
    print("The following error occurred:")
    print(issue)

Dane wyjściowe powinny zawierać listę plików, jak poniżej:

s3.ObjectSummary(bucket_name='my-files', key='some-directory/')
s3.ObjectSummary(bucket_name='my-files', key='some-directory/another-file.txt')
s2.ObjectSummary(bucket_name='my-files', key='some-directory/first-file.txt')
s3.ObjectSummary(bucket_name='my-files', key='some-directory/some-other-directory/')
s3.ObjectSummary(bucket_name='my-files', key='some-directory/some-other-directory/some-other-file.txt')
s3.ObjectSummary(bucket_name='my-files', key='some-directory/some-other-directory/yet-another-file.txt')
s3.ObjectSummary(bucket_name='my-files', key='text-file.txt')

Jeśli w bucket nie ma żadnych plików, dane wyjściowe powinny być puste.

Rozwiązywanie problemów z listowaniem plików w bucket

Brak dostępu do bucketu

Jeśli para kluczy nie ma dostępu do wybranego bucketu, powinien pojawić się następujący błąd:

botocore.exceptions.ClientError: An error occurred (AccessDenied) when calling the ListObjects operation: Unknown

W takim przypadku należy wybrać inny bucket lub inną parę kluczy, jeśli masz taką, która może uzyskać do niego dostęp.

Bucket nie istnieje

Jeśli wybrany bucket nie istnieje, błąd może wyglądać następująco:

botocore.errorfactory.NoSuchBucket: An error occurred (NoSuchBucket) when calling the ListObjects operation: Unknown

Jeśli potrzebujesz bucketa, który używa tej nazwy, spróbuj go utworzyć zgodnie z opisem w sekcji Tworzenie kontenera powyżej.

Wyświetlanie plików z określonej ścieżki w bucket

Ten przykład wyświetli tylko obiekty z określonej ścieżki. Istnieją dwie zasady, których należy przestrzegać dla zmiennej ścieżka:

  • Zakończ ukośnikiem

  • Nie zaczynaj od ukośnika.

Jak zawsze, dodaj nazwę bucketu do zmiennej name.

boto3_list_files_from_path

import boto3

access_key = ''
secret_key = ''

name = 'boto3'
endpoint_url='https://s3.waw4-1.cloudferro.com'
region_name='US'
path = ''

try:

    s3=boto3.resource('s3',
        aws_access_key_id=access_key,
        aws_secret_access_key=secret_key,
        endpoint_url=endpoint_url,
        region_name=region_name)

    bucket=s3.Bucket(name)
    for obj in bucket.objects.filter(Prefix=path):
        print(obj)

except Exception as issue:
 print("The following error occurred:")
 print(issue)

Standardowe wyjście powinno zostać wygenerowane, ale jeśli nie ma plików pod wybraną ścieżką, wyjście będzie puste.

Przesyłanie pliku do bucketu

Aby przesłać plik do kontenera, dodaj następującą zawartość do zmiennych:

Nazwa zmiennej

Co powinna zawierać

bucket_name

Nazwa bucketu, do którego zostanie wysłany plik.

source_file

Lokalizacja pliku, który chcesz przesłać w swoim lokalnym systemie plików.

destination_file

Ścieżka docelowa w kontenerze dla przesyłanego pliku. Powinna zawierać wyłącznie litery, cyfry, myślniki i ukośniki.

Dwa zastrzeżenia dotyczące zmiennej destination:

  1. Zakończ ją nazwą przesyłanego pliku i

  2. Nie zaczynaj ani nie kończ go ukośnikiem.

To jest kod:

boto3_upload_files.py

import boto3
import requests
from requests_aws4auth import AWS4Auth

access_key = ''
secret_key = ''

bucket_name = 'boto3'
source_file = 'onefile.txt'
destination_file = 'onefile.txt'

import requests
from requests_aws4auth import AWS4Auth

def upload_file_to_object_storage(bucket_name, source_path, destination_path, access_key, secret_key):
    """
    Upload a file to a CloudFerro S3 bucket.

    Args:
        bucket_name (str): Name of the destination S3 bucket.
        source_path (str): Local path to the file to upload.
        destination_path (str): Destination path in the bucket.
        access_key (str): AWS access key.
        secret_key (str): AWS secret key.

    Returns:
        response (requests.Response): The HTTP response object.
    """
    region = 'RegionOne'
    service = 's3'
    endpoint = 'https://s3.waw4-1.cloudferro.com'

    auth = AWS4Auth(access_key, secret_key, region, service)


    with open(source_path, 'rb') as file_data:
        file_content = file_data.read()
    headers = {
        'Content-Length': str(len(file_content)),
    }

    url = f'{endpoint}/{bucket_name}/{destination_path}'

    response = żądania.put(url, data=file_content, headers=headers, auth=auth)
    return response

#demo
response = upload_file_to_object_storage(bucket_name, source_file, destination_file, access_key, secret_key)

jeśli response.status_code == 200:
    print(f "Uploaded {source_file} successfully to {bucket_name}/{destination_file}")
inaczej:
    print(f "Upload failed with status {response.status_code}: {response.text}")

Spowoduje to przesłanie pliku onefile.txt z lokalnego folderu do bucketu o nazwie boto3; przesłany plik będzie również nazywał się onefile.txt. Sama funkcja, upload_file_to_object_storage, jest wielokrotnego użytku i może być używana w różnych kontekstach; tutaj główny program po prostu drukuje jednowierszowy raport o tym, co się stało. Można również wykonać funkcję

  • zgłasza wyjątek w przypadku błędu przesyłania,

  • ponawia próbę automatycznie,

  • przesłać wiele plików w partii lub

  • rejestrowanie sukcesu/porażki w pliku

ale to wykracza poza zakres tego artykułu.

Rozwiązywanie problemów z przesyłaniem pliku do bucketu

Plik, który chcesz przesłać nie istnieje

Jeśli podałeś nieistniejący plik w zmiennej source, powinieneś otrzymać błąd podobny do tego:

The following error occurred:
[Errno 2] No such file or directory: 'here/wrong-file.txt'

Aby rozwiązać problem, podaj prawidłowy plik i spróbuj ponownie.

Pobieranie pliku z bucketu

Aby zapisać plik z bucketu na lokalnym dysku twardym, wprowadź wartości poniższych zmiennych i uruchom poniższy kod:

Nazwa zmiennej

Co powinna zawierać

name

Nazwa bucketu, z którego chcesz pobrać plik.

source

Ścieżka w kontenerze, z której chcesz pobrać swój plik.

destination

Ścieżka w twoim lokalnym systemie plików, pod którą chcesz zapisać swój plik.

Nie zaczynaj ani nie kończ zmiennej source ukośnikiem.

boto3_download_file.py

import boto3

access_key = ''
secret_key = ''

name = 'boto3'
endpoint_url='https://s3.waw4-1.cloudferro.com'
region_name='US'
source='onefile.txt'
destination='onefile2.txt'

try:

    s3=boto3.resource('s3',
        aws_access_key_id=access_key,
        aws_secret_access_key=secret_key,
        endpoint_url=endpoint_url,
        region_name=region_name)

    bucket=s3.Bucket(name)

    bucket.download_file(source, destination)

except Exception as issue:
    print("The following error occurred:")
    print(issue)

Pomyślne wykonanie tego kodu nie powinno dać żadnych wyników.

Rozwiązywanie problemów z przesyłaniem pliku do bucketu

Plik nie istnieje w bucket.

Jeśli wybrany plik nie istnieje w bucket, powinien pojawić się następujący błąd:

The following error occurred:
An error occurred (404) when calling the HeadObject operation: Not Found

Aby rozwiązać ten problem, należy upewnić się, że podano prawidłowy plik.

Usuwanie pliku z bucketu

Aby usunąć plik ze swojego bucketu, podaj nazwę bucketu w zmiennej name, a jego pełną ścieżkę w zmiennej ścieżka.

boto3_remove_file_from_bucket.py

import boto3

access_key = ''
secret_key = ''

name = 'boto3'
endpoint_url='https://s3.waw4-1.cloudferro.com'
region_name='US'
path = 'onefile.txt'

try:

    s3=boto3.resource('s3',
        aws_access_key_id=access_key,
        aws_secret_access_key=secret_key,
        endpoint_url=endpoint_url,
        region_name=region_name)

    s3.Object(name, path).delete()

except Exception as issue:
    print("The following error occurred:")
    print(issue)

Pomyślne wykonanie tego kodu nie powinno dać żadnych wyników.

Usuwanie bucketu

Aby usunąć bucket, należy najpierw usunąć z niego wszystkie obiekty. Gdy bucket będzie pusty, zdefiniuj zmienną name dla bucketu, który chcesz usunąć i wykonaj poniższy kod:

boto3_remove_bucket.py

import boto3

access_key = ''
secret_key = ''

name = 'boto3'
endpoint_url='https://s3.waw4-1.cloudferro.com'
region_name='US'

try:

    s3=boto3.resource('s3',
        aws_access_key_id=access_key,
        aws_secret_access_key=secret_key,
        endpoint_url=endpoint_url,
        region_name=region_name)

    s3.Bucket(name).delete()

except Exception as issue:
    print("The following error occurred:")
    print(issue)

Pomyślne wykonanie tego kodu nie powinno dać żadnych wyników.

Rozwiązywanie problemów z usuwaniem bucketu

Bucket nie istnieje lub jest niedostępny dla twojej pary kluczy.

Jeśli bucket nie istnieje lub jest niedostępny dla twojej pary kluczy, powinieneś otrzymać następujące dane wyjściowe:

The following error occurred:
An error occurred (NoSuchBucket) when calling the DeleteBucket operation: Unknown

Bucket nie jest pusty

Jeśli bucket nie jest pusty, nie można go usunąć. Zostanie wyświetlony komunikat:

The following error occurred:
An error occurred (BucketNotEmpty) when calling the DeleteBucket operation: Unknown

Aby rozwiązać ten problem, usuń wszystkie obiekty z bucketu i spróbuj ponownie.

Ogólne rozwiązywanie problemów

Brak połączenia z punktem końcowym

Jeśli nie masz połączenia z punktem końcowym (na przykład z powodu utraty połączenia z Internetem), powinieneś otrzymać wynik podobny do tego:

The following error occurred:
Could not connect to the endpoint URL: "https://s3.waw4-1.cloudferro.com/"

Jeśli tak jest, upewnij się, że masz połączenie z Internetem. Jeśli masz pewność, że masz połączenie z Internetem i nie ogłoszono przestoju dla NSIS Cloud object storage, skontaktuj się z obsługą klienta NSIS Cloud: Helpdesk i wsparcie

Błędne dane uwierzytelniające

Jeśli klucze access i/lub secret są nieprawidłowe, wyświetlony zostanie następujący komunikat:

The following error occurred:
An error occurred (InvalidAccessKeyId) when calling the ListBuckets operation: Unknown

Aby dowiedzieć się, jak uzyskać ważne dane uwierzytelniające, patrz Warunek wstępny nr 2.

Bucket nie istnieje lub jest niedostępny dla twojej pary kluczy.

Jeśli bucket wybrany dla tego kodu nie istnieje lub jest niedostępny dla pary kluczy, można uzyskać różne wyniki w zależności od wykonywanego polecenia. Poniżej znajdują się dwa takie przykłady:

None
The following error occurred:
An error occurred (NoSuchBucket) when calling the DeleteObject operation: Unknown

Co można zrobić dalej

boto3 może być również używane do uzyskiwania dostępu do repozytorium EODATA na maszynach wirtualnych hostowanych w chmurze NSIS Cloud. Dowiedz się więcej tutaj: Jak uzyskać dostęp do EODATA za pomocą boto3 na NSIS

Innym narzędziem do uzyskiwania dostępu do przechowywania obiektów w chmurze jest s3cmd: Jak uzyskać dostęp do object storage z NSIS za pomocą s3cmd.