View a markdown version of this page

Amazon S3 유틸리티 - AWS SDK for Go v2

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

Amazon S3 유틸리티

Amazon S3 Transfer Managers

Amazon S3 업로드 및 다운로드 관리자는 큰 객체를 분할할 수 있으므로 객체를 여러 파트로 병렬 전송할 수 있습니다. 이렇게 하면 중단된 전송을 쉽게 재개할 수 있습니다.

Amazon S3 Upload Manager

Amazon S3 Upload Manager는 파일을 더 작은 파트로 분할하고 병렬로 업로드할 수 있는지 여부를 결정합니다. 병렬 업로드 수와 업로드된 파트의 크기를 사용자 지정할 수 있습니다.

다음 예제에서는 Amazon S3 Uploader를 사용하여 파일을 업로드합니다. Uploader 사용 방법은 s3.PutObject() 작업과 비슷합니다.

import "context" import "github.com/aws/aws-sdk-go-v2/config" import "github.com/aws/aws-sdk-go-v2/service/s3" import "github.com/aws/aws-sdk-go-v2/feature/s3/manager" // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Printf("error: %v", err) return } client := s3.NewFromConfig(cfg) uploader := manager.NewUploader(client) result, err := uploader.Upload(context.TODO(), &s3.PutObjectInput{ Bucket: aws.String("amzn-s3-demo-bucket"), Key: aws.String("my-object-key"), Body: uploadFile, })

구성 옵션

NewUploader를 사용하여 Uploader 인스턴스를 인스턴스화할 때 여러 구성 옵션을 지정하여 객체 업로드 방법을 사용자 지정할 수 있습니다. 옵션은 NewUploader에 하나 이상의 인수를 제공하여 재정의됩니다. 이 옵션에는 다음이 포함됩니다.

  • PartSize - 업로드할 각 파트의 버퍼 크기를 바이트 단위로 지정합니다. 파트당 최소 크기는 5MiB입니다.

  • Concurrency - 병렬로 업로드할 파트의 수를 지정합니다.

  • LeavePartsOnError - Amazon S3에 성공적으로 업로드된 파트를 남길지 여부를 나타냅니다.

Concurrency 값은 주어진 Upload 직접 호출에 대해 발생할 수 있는 동시 파트 업로드 수를 제한합니다. 이는 글로벌 클라이언트 동시성 제한이 아닙니다. PartSizeConcurrency 구성 값을 조정하여 최적의 구성을 찾습니다. 예를 들어, 연결 대역폭이 높은 시스템은 더 큰 파트와 더 많은 업로드를 병렬로 전송할 수 있습니다.

예를 들어, 애플리케이션에서 UploaderConcurrency 설정을 5로 구성합니다. 애플리케이션이 서로 다른 두 개의 고루틴에서 Upload를 직접 호출하면 결과는 10개의 동시 파트 업로드(고루틴 2 * Concurrency 5)입니다.

주의

애플리케이션 리소스 소진을 방지하기 위해 애플리케이션은 동시 직접 호출을 Upload로 제한해야 합니다.

다음은 Uploader 생성 중에 기본 파트 크기를 설정하는 예제입니다.

uploader := manager.NewUploader(client, func(u *Uploader) { u.PartSize = 10 * 1024 * 1024, // 10 MiB })

Uploader 및 구성에 대한 자세한 내용은 AWS SDK for Go API 참조의 업로더를 참조하세요.

PutObjectInput 본문 필드(io.ReadSeeker와 io.Reader 비교)

s3.PutObjectInput 구조체의 Body 필드는 io.Reader 유형입니다. 그러나 이 필드를 io.ReadSeekerio.ReaderAt 인터페이스를 모두 충족하는 유형으로 채우면 호스트 환경의 애플리케이션 리소스 사용률을 개선할 수 있습니다. 다음 예제에서는 두 인터페이스를 모두 충족하는 유형 ReadSeekerAt을 생성합니다.

type ReadSeekerAt interface { io.ReadSeeker io.ReaderAt }

io.Reader 유형의 경우 파트를 업로드하려면 먼저 리더의 바이트를 메모리에 버퍼링해야 합니다. PartSize 또는 Concurrency 값을 늘리면 Uploader에 필요한 메모리(RAM)가 크게 증가합니다. 필요한 메모리는 약 PartSize * Concurrency입니다. 예를 들어 PartSize에 100MB를 지정하고 Concurrency에 10을 지정하려면 최소 1GB가 필요합니다.

io.Reader 유형은 바이트를 읽기 전에 크기를 결정할 수 없으므로 Uploader는 업로드할 파트 수를 계산할 수 없습니다. 따라서 PartSize를 너무 낮게 설정하면 Uploader가 대용량 파일의 Amazon S3 업로드 한도인 10,000파트에 도달할 수 있습니다. 10,000개 이상의 파트를 업로드하려고 하면 업로드가 중지되고 오류가 반환됩니다.

ReadSeekerAt 유형을 구현하는 body 값의 경우, Uploader는 Amazon S3로 전송하기 전에 본문 콘텐츠를 메모리에 버퍼링하지 않습니다. Uploader는 파일을 Amazon S3에 업로드하기 전에 예상 파트 수를 계산합니다. PartSize의 현재 값에 파일을 업로드하는 데 10,000개 이상의 파트가 필요한 경우 Uploader는 필요한 파트 수가 적도록 파트 크기 값을 늘립니다.

실패한 업로드 처리

Amazon S3에 대한 업로드가 실패하면 기본적으로 Uploader는 Amazon S3 AbortMultipartUpload 작업을 사용하여 업로드된 파트를 제거합니다. 이 기능은 실패한 업로드가 Amazon S3 스토리지를 사용하지 않도록 합니다.

Uploader가 성공적으로 업로드된 파트를 삭제하지 않도록 LeavePartsOnError를 true로 설정할 수 있습니다. 이는 부분적으로 완료된 업로드를 재개하는 데 유용합니다. 업로드된 파트에서 작동하려면 실패한 업로드의 UploadID를 가져와야 합니다. 다음 예제에서는 manager.MultiUploadFailure 오류 인터페이스 유형을 사용하여 UploadID를 가져오는 방법을 보여줍니다.

result, err := uploader.Upload(context.TODO(), &s3.PutObjectInput{ Bucket: aws.String("amzn-s3-demo-bucket"), Key: aws.String("my-object-key"), Body: uploadFile, }) output, err := u.upload(input) if err != nil { var mu manager.MultiUploadFailure if errors.As(err, &mu) { // Process error and its associated uploadID fmt.Println("Error:", mu) _ = mu.UploadID() // retrieve the associated UploadID } else { // Process error generically fmt.Println("Error:", err.Error()) } return }

업로드당 업로더 옵션 재정의

메서드에 하나 이상의 인수를 제공하여 Upload를 직접 호출할 때 Uploader 옵션을 재정의할 수 있습니다. 이러한 재정의는 동시성에 안전한 수정 사항이며 진행 중인 업로드 또는 후속 관리자 Upload 직접 호출에는 영향을 주지 않습니다. 예를 들어 특정 업로드 요청에 대한 PartSize 구성을 재정의하려면 다음을 수행합니다.

params := &s3.PutObjectInput{ Bucket: aws.String("amzn-s3-demo-bucket"), Key: aws.String("my-key"), Body: myBody, } resp, err := uploader.Upload(context.TODO(), params, func(u *manager.Uploader) { u.PartSize = 10 * 1024 * 1024, // 10 MiB })

예제

Amazon S3로 폴더 업로드

다음 예제에서는 path/filepath 패키지를 사용하여 파일 목록을 재귀적으로 수집하고 지정된 Amazon S3 버킷에 업로드합니다. Amazon S3 객체의 키에는 파일의 상대 경로 접두사가 붙습니다.

package main import ( "context" "log" "os" "path/filepath" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/feature/s3/manager" "github.com/aws/aws-sdk-go-v2/service/s3" ) var ( localPath string bucket string prefix string ) func init() { if len(os.Args) != 4 { log.Fatalln("Usage:", os.Args[0], "<local path> <bucket> <prefix>") } localPath = os.Args[1] bucket = os.Args[2] prefix = os.Args[3] } func main() { walker := make(fileWalk) go func() { // Gather the files to upload by walking the path recursively if err := filepath.Walk(localPath, walker.Walk); err != nil { log.Fatalln("Walk failed:", err) } close(walker) }() cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Fatalln("error:", err) } // For each file found walking, upload it to Amazon S3 uploader := manager.NewUploader(s3.NewFromConfig(cfg)) for path := range walker { rel, err := filepath.Rel(localPath, path) if err != nil { log.Fatalln("Unable to get relative path:", path, err) } file, err := os.Open(path) if err != nil { log.Println("Failed opening file", path, err) continue } defer file.Close() result, err := uploader.Upload(context.TODO(), &s3.PutObjectInput{ Bucket: &bucket, Key: aws.String(filepath.Join(prefix, rel)), Body: file, }) if err != nil { log.Fatalln("Failed to upload", path, err) } log.Println("Uploaded", path, result.Location) } } type fileWalk chan string func (f fileWalk) Walk(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() { f <- path } return nil }

Download Manager

Amazon S3 Downloader Manager는 파일을 더 작은 파트로 분할하고 병렬로 다운로드할 수 있는지 여부를 결정합니다. 병렬 다운로드 수와 다운로드된 파트의 크기를 사용자 지정할 수 있습니다.

예제: 파일 다운로드

다음 예제에서는 Amazon S3 Downloader를 사용하여 파일을 다운로드합니다. Downloader 사용 방법은 s3.GetObject 작업과 비슷합니다.

import "context" import "github.com/aws/aws-sdk-go-v2/aws" import "github.com/aws/aws-sdk-go-v2/config" import "github.com/aws/aws-sdk-go-v2/service/s3" import "github.com/aws/aws-sdk-go-v2/feature/s3/manager" // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Println("error:", err) return } client := s3.NewFromConfig(cfg) downloader := manager.NewDownloader(client) numBytes, err := downloader.Download(context.TODO(), downloadFile, &s3.GetObjectInput{ Bucket: aws.String("amzn-s3-demo-bucket"), Key: aws.String("my-key"), })

downloadFile 파라미터는 io.WriterAt 유형입니다. WriterAt 인터페이스를 사용하면 Downloader가 파일의 여러 파트를 병렬로 쓸 수 있습니다.

구성 옵션

Downloader 인스턴스를 인스턴스화할 때 구성 옵션을 지정하여 객체 다운로드 방법을 사용자 지정할 수 있습니다.

  • PartSize - 다운로드할 각 파트의 버퍼 크기를 바이트 단위로 지정합니다. 파트당 최소 크기는 5MB입니다.

  • Concurrency - 병렬로 다운로드할 파트의 수를 지정합니다.

Concurrency 값은 주어진 Download 직접 호출에 대해 발생할 수 있는 동시 파트 다운로드 수를 제한합니다. 이는 글로벌 클라이언트 동시성 제한이 아닙니다. PartSizeConcurrency 구성 값을 조정하여 최적의 구성을 찾습니다. 예를 들어, 연결 대역폭이 높은 시스템은 더 큰 파트와 더 많은 다운로드를 병렬로 수신할 수 있습니다.

예를 들어, 애플리케이션에서 DownloaderConcurrency5로 구성합니다. 그러면 애플리케이션이 서로 다른 두 개의 고루틴에서 Download를 직접 호출하고 결과는 10개의 동시 파트 다운로드(고루틴 2 * Concurrency 5)가 됩니다.

주의

애플리케이션 리소스 소진을 방지하기 위해 애플리케이션은 동시 직접 호출을 Download로 제한해야 합니다.

Downloader 및 기타 구성 옵션에 대한 자세한 내용은 AWS SDK for Go API 참조의 manager.Downloader를 참조하세요.

다운로드당 다운로더 옵션 재정의

메서드에 하나 이상의 함수 인수를 제공하여 Download를 직접 호출할 때 Downloader 옵션을 재정의할 수 있습니다. 이러한 재정의는 동시성에 안전한 수정 사항이며 진행 중인 업로드 또는 후속 관리자 Download 직접 호출에는 영향을 주지 않습니다. 예를 들어 특정 업로드 요청에 대한 PartSize 구성을 재정의하려면 다음을 수행합니다.

params := &s3.GetObjectInput{ Bucket: aws.String("amzn-s3-demo-bucket"), Key: aws.String("my-key"), } resp, err := downloader.Download(context.TODO(), targetWriter, params, func(u *manager.Downloader) { u.PartSize = 10 * 1024 * 1024, // 10 MiB })
예제
버킷의 모든 객체 다운로드

다음 예제에서는 페이지 매김을 사용하여 Amazon S3 버킷에서 객체 목록을 수집합니다. 그런 다음 각 객체를 로컬 파일로 다운로드합니다.

package main import ( "context" "fmt" "log" "os" "path/filepath" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/feature/s3/manager" "github.com/aws/aws-sdk-go-v2/service/s3" ) var ( Bucket = "amzn-s3-demo-bucket" // Download from this bucket Prefix = "logs/" // Using this key prefix LocalDirectory = "s3logs" // Into this directory ) func main() { cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Fatalln("error:", err) } client := s3.NewFromConfig(cfg) manager := manager.NewDownloader(client) paginator := s3.NewListObjectsV2Paginator(client, &s3.ListObjectsV2Input{ Bucket: &Bucket, Prefix: &Prefix, }) for paginator.HasMorePages() { page, err := paginator.NextPage(context.TODO()) if err != nil { log.Fatalln("error:", err) } for _, obj := range page.Contents { if err := downloadToFile(manager, LocalDirectory, Bucket, aws.ToString(obj.Key)); err != nil { log.Fatalln("error:", err) } } } } func downloadToFile(downloader *manager.Downloader, targetDirectory, bucket, key string) error { // Create the directories in the path file := filepath.Join(targetDirectory, key) if err := os.MkdirAll(filepath.Dir(file), 0775); err != nil { return err } // Set up the local file fd, err := os.Create(file) if err != nil { return err } defer fd.Close() // Download the file using the AWS SDK for Go fmt.Printf("Downloading s3://%s/%s to %s...\n", bucket, key, file) _, err = downloader.Download(context.TODO(), fd, &s3.GetObjectInput{Bucket: &bucket, Key: &key}) return err }

GetBucketRegion

GetBucketRegion은 Amazon S3 버킷의 AWS 리전 위치를 결정하는 유틸리티 함수입니다. GetBucketRegion는 Amazon S3 클라이언트를 가져와 클라이언트의 구성된 리전과 연결된 AWS 파티션 내에서 요청된 버킷의 위치를 결정하는 데 사용합니다.

예를 들어 버킷 amzn-s3-demo-bucket의 리전을 찾으려면 다음을 수행합니다.

cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Println("error:", err) return } bucket := "amzn-s3-demo-bucket" region, err := manager.GetBucketRegion(ctx, s3.NewFromConfig(cfg), bucket) if err != nil { var bnf manager.BucketNotFound if errors.As(err, &bnf) { log.Printf("unable to find bucket %s's Region\n", bucket) } else { log.Println("error:", err) } return } fmt.Printf("Bucket %s is in %s region\n", bucket, region)

GetBucketRegion이 버킷의 위치를 확인할 수 없는 경우 함수는 예제와 같이 BucketNotFound 오류 유형을 반환합니다.

확인할 수 없는 스트리밍 입력

PutObjectUploadPart와 같은 API 작업의 경우 Amazon S3 클라이언트는 Body 입력 파라미터의 값이 기본적으로 io.Seeker 인터페이스를 구현할 것으로 예상합니다. 클라이언트는 io.Seeker 인터페이스를 사용하여 업로드할 값의 길이를 결정하고 요청 서명에 대한 페이로드 해시를 계산합니다. Body 입력 파라미터 값이 io.Seeker를 구현하지 않으면 애플리케이션에 오류가 발생합니다.

operation error S3: PutObject, failed to compute payload hash: failed to seek body to start, request stream is not seekable

기능 옵션을 사용하여 작업 메서드의 미들웨어를 수정하여 이 동작을 변경할 수 있습니다. WithAPIOptions 헬퍼는 0개 이상의 미들웨어 변형기에 대한 기능 옵션을 반환합니다. 클라이언트가 페이로드 해시를 계산하지 않도록 하고 서명되지 않은 페이로드 요청 서명을 사용하려면 v4.SwapComputePayloadSHA256ForUnsignedPayloadMiddleware를 추가합니다.

resp, err := client.PutObject(context.TODO(), &s3.PutObjectInput{ Bucket: &bucketName, Key: &objectName, Body: bytes.NewBuffer([]byte(`example object!`)), ContentLength: 15, // length of body }, s3.WithAPIOptions( v4.SwapComputePayloadSHA256ForUnsignedPayloadMiddleware, ))
주의

Amazon S3에서는 버킷에 업로드된 모든 객체에 콘텐츠 길이를 제공해야 합니다. Body 입력 파라미터는 io.Seeker 인터페이스를 구현하지 않으므로 클라이언트는 요청에 대한 ContentLength 파라미터를 계산할 수 없습니다. 파라미터는 애플리케이션에서 제공해야 합니다. ContentLength 파라미터가 제공되지 않으면 요청이 실패합니다.

탐색할 수 없고 알려진 길이가 없는 업로드에는 SDK의 Amazon S3 Upload Manager를 사용합니다.