Go (AWS SDK v2) i HostArena S3: primeri koda
AWS SDK za Go (verzija 2) je zvanična biblioteka za rad sa S3 skladištem iz Go programa. Sa HostArena S3 storage je koristite isto kao sa Amazon S3, uz podešavanje adrese servera (BaseEndpoint), regiona garage i path-style adresa (UsePathStyle).
Šta vam je potrebno
| Podatak | Vrednost | Gde ga nalazite |
|---|---|---|
| Endpoint | https://s3.hostarena.rs | Isti za sve korisnike |
| Region | garage | Obavezno tačno ovako |
| Bucket | vas-bucket | HostArena korisnički panel, kod S3 usluge |
| Access Key | VAS_ACCESS_KEY | HostArena korisnički panel, kod S3 usluge |
| Secret Key | VAS_SECRET_KEY | HostArena korisnički panel, kod S3 usluge |
Potreban vam je Go, neka od aktuelnih verzija.
Koraci
Napravite projekat:
mkdir s3-primer cd s3-primer go mod init primerKljučeve ne upisujte direktno u kod. Postavite ih kao promenljive okruženja. Na Linux-u i macOS-u:
export AWS_ACCESS_KEY_ID="VAS_ACCESS_KEY" export AWS_SECRET_ACCESS_KEY="VAS_SECRET_KEY"U Windows PowerShell-u:
$env:AWS_ACCESS_KEY_ID="VAS_ACCESS_KEY" $env:AWS_SECRET_ACCESS_KEY="VAS_SECRET_KEY"Ove komande važe za otvoreni terminal. Na serveru ih obično upisujete u podešavanja servisa, u
.envfajl koji se ne objavljuje ili u alat za tajne podatke.SDK ove promenljive čita sam, preko
config.LoadDefaultConfig.Napravite fajl
main.go. Primer otprema fajl, ispisuje spisak fajlova (i kada ih ima više od 1000) i preuzima fajl tako da ga stvarno snimi na disk. Svaka greška se proverava, a S3 kod greške se ispisuje čitljivo:package main import ( "context" "errors" "fmt" "io" "log" "os" "strings" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/smithy-go" ) const bucket = "vas-bucket" func main() { ctx := context.Background() // Ključevi se čitaju iz promenljivih okruženja // AWS_ACCESS_KEY_ID i AWS_SECRET_ACCESS_KEY. cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("garage")) if err != nil { log.Fatalf("greška u podešavanju: %v", err) } client := s3.NewFromConfig(cfg, func(o *s3.Options) { o.BaseEndpoint = aws.String("https://s3.hostarena.rs") o.UsePathStyle = true }) if err := uploadFile(ctx, client, "bekap.zip", "bekapi/bekap.zip"); err != nil { log.Fatal(err) } if err := listFiles(ctx, client); err != nil { log.Fatal(err) } if err := downloadFile(ctx, client, "bekapi/bekap.zip", "vraceno.zip"); err != nil { log.Fatal(err) } } // Otpremanje lokalnog fajla u bucket. func uploadFile(ctx context.Context, client *s3.Client, localPath, key string) error { file, err := os.Open(localPath) if err != nil { return fmt.Errorf("ne mogu da otvorim %s: %w", localPath, err) } defer file.Close() _, err = client.PutObject(ctx, &s3.PutObjectInput{ Bucket: aws.String(bucket), Key: aws.String(key), Body: file, }) if err != nil { return fmt.Errorf("otpremanje nije uspelo: %w", describe(err)) } fmt.Println("Otpremljeno:", key) return nil } // Spisak fajlova u bucketu (radi i za više od 1000 fajlova). func listFiles(ctx context.Context, client *s3.Client) error { p := s3.NewListObjectsV2Paginator(client, &s3.ListObjectsV2Input{ Bucket: aws.String(bucket), }) for p.HasMorePages() { page, err := p.NextPage(ctx) if err != nil { return fmt.Errorf("listanje nije uspelo: %w", describe(err)) } for _, obj := range page.Contents { fmt.Printf("%s %d bajtovan", aws.ToString(obj.Key), aws.ToInt64(obj.Size)) } } return nil } // Preuzimanje fajla iz bucketa i snimanje na disk. func downloadFile(ctx context.Context, client *s3.Client, key, localPath string) error { out, err := client.GetObject(ctx, &s3.GetObjectInput{ Bucket: aws.String(bucket), Key: aws.String(key), }) if err != nil { return fmt.Errorf("preuzimanje nije uspelo: %w", describe(err)) } defer out.Body.Close() f, err := os.Create(localPath) if err != nil { return fmt.Errorf("ne mogu da napravim %s: %w", localPath, err) } defer f.Close() n, err := io.Copy(f, out.Body) if err != nil { return fmt.Errorf("snimanje nije uspelo: %w", err) } fmt.Printf("Snimljeno: %s (%d bajtova)n", localPath, n) return nil } // Brisanje fajla iz bucketa. func deleteFile(ctx context.Context, client *s3.Client, key string) error { _, err := client.DeleteObject(ctx, &s3.DeleteObjectInput{ Bucket: aws.String(bucket), Key: aws.String(key), }) if err != nil { return fmt.Errorf("brisanje nije uspelo: %w", describe(err)) } return nil } // Izvlači S3 kod greške (npr. NoSuchKey, AccessDenied) radi lakšeg čitanja. func describe(err error) error { var apiErr smithy.APIError if errors.As(err, &apiErr) { return fmt.Errorf("%s: %s", apiErr.ErrorCode(), strings.TrimSpace(apiErr.ErrorMessage())) } return err }Preuzmite potrebne biblioteke i pokrenite program (u istom terminalu u kom ste postavili ključeve):
go mod tidy go run .go mod tidysam preuzima sve pakete navedene uimportdelu.
Korisno je znati
- Kod preuzimanja,
out.Bodyje tok podataka.io.Copyga upisuje u fajl, adefer out.Body.Close()zatvara vezu. Bez toga fajl ne bi bio snimljen. - Funkcija
deleteFileje u primeru definisana, ali se ne poziva izmain. Pozovite je samo kada zaista želite da obrišete fajl. - Za velike fajlove postoji paket
github.com/aws/aws-sdk-go-v2/feature/s3/manager, koji fajl deli na delove.
Pažnja: DeleteObject trajno briše fajl i ne pita za potvrdu. Obrisani fajl se ne može vratiti.
Provera da radi
Stavite mali fajl bekap.zip u folder projekta i pokrenite go run .. Ispravan ispis je „Otpremljeno”, spisak fajlova i „Snimljeno: vraceno.zip”. Ako program stane sa porukom o grešci, na početku poruke je S3 kod greške.
Najčešći problemi
AuthorizationHeaderMalformed
Region nije dobar. Mora biti tačno garage. Sa us-east-1 ili bilo kojim drugim AWS regionom server odbija zahtev.
InvalidAccessKeyId, AccessDenied ili SignatureDoesNotMatch
Proverite da li su promenljive okruženja postavljene u istom terminalu ili servisu u kom pokrećete program i da ključevi nemaju razmak na početku ili kraju. Ključeve proverite u HostArena korisničkom panelu, kod S3 usluge.
NoSuchBucket
Naziv bucketa nije tačan. Prepišite ga iz HostArena korisničkog panela. Endpoint ostaje https://s3.hostarena.rs, bez naziva bucketa u adresi.
Greška vezana za checksum pri otpremanju
Novije verzije AWS SDK-a same dodaju kontrolni zbir (checksum) svakom otpremanju. Ako dobijete grešku koja pominje checksum, dodajte u podešavanja klijenta sledeće dve opcije:
client := s3.NewFromConfig(cfg, func(o *s3.Options) {
o.BaseEndpoint = aws.String("https://s3.hostarena.rs")
o.UsePathStyle = true
o.RequestChecksumCalculation = aws.RequestChecksumCalculationWhenRequired
o.ResponseChecksumValidation = aws.ResponseChecksumValidationWhenRequired
})Zaključak
Isti princip važi i za druge jezike: PHP, Python (boto3) i Node.js. Tehnički detalji o adresama i potpisivanju zahteva nalaze se u uputstvu HostArena S3 API: adrese, potpisivanje i kompatibilnost. Nemate S3 uslugu? Pogledajte pakete na stranici HostArena S3 storage. Ako zapne, otvorite tiket u korisničkom panelu.
