func (d Downloader) DownloadWithContext(ctx aws.Context, w io.WriterAt, input *s3.GetObjectInput, options ...func(*Downloader)) (n int64, err error) {
	impl := downloader{w: w, in: input, cfg: d, ctx: ctx}
	for _, option := range options {
		option(&impl.cfg)
	}
	impl.cfg.RequestOptions = append(impl.cfg.RequestOptions, request.WithAppendUserAgent("S3Manager"))
	if s, ok := d.S3.(maxRetrier); ok {
		impl.partBodyMaxRetries = s.MaxRetries()
	}
	impl.totalBytes = -1
	if impl.cfg.Concurrency == 0 {
		impl.cfg.Concurrency = DefaultDownloadConcurrency
	}
	if impl.cfg.PartSize == 0 {
		impl.cfg.PartSize = DefaultDownloadPartSize
	}
	return impl.download()
}
DownloadWithContext downloads an object in S3 and writes the payload into w
using concurrent GET requests. The n int64 returned is the size of the object downloaded
in bytes.

DownloadWithContext is the same as Download with the additional support for
Context input parameters. The Context must not be nil. A nil Context will
cause a panic. Use the Context to add deadlining, timeouts, etc. The
DownloadWithContext may create sub-contexts for individual underlying
requests.

Additional functional options can be provided to configure the individual
download. These options are copies of the Downloader instance Download is
called from. Modifying the options will not impact the original Downloader
instance. Use the WithDownloaderRequestOptions helper function to pass in request
options that will be applied to all API operations made with this downloader.

The w io.WriterAt can be satisfied by an os.File to do multipart concurrent
downloads, or in memory []byte wrapper using aws.WriteAtBuffer.

Specifying a Downloader.Concurrency of 1 will cause the Downloader to
download the parts from S3 sequentially.

It is safe to call this method concurrently across goroutines.

If the GetObjectInput's Range value is provided that will cause the downloader
to perform a single GetObjectInput request for that object's range. This will
caused the part size, and concurrency configurations to be ignored.
package s3manager
import (
	"fmt"
	"io"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/awsutil"
	"github.com/aws/aws-sdk-go/aws/client"
	"github.com/aws/aws-sdk-go/aws/request"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3iface"
)
const DefaultDownloadPartSize = 1024 * 1024 * 5
const DefaultDownloadConcurrency = 5
type errReadingBody struct {
	err error
}
func (e *errReadingBody) Error() string {
	return fmt.Sprintf("failed to read part body: %v", e.err)
}
func (e *errReadingBody) Unwrap() error {
	return e.err
}
type Downloader struct {
	PartSize int64
	Concurrency int
	S3 s3iface.S3API
	RequestOptions []request.Option
	BufferProvider WriterReadFromProvider
}
func WithDownloaderRequestOptions(opts ...request.Option) func(*Downloader) {
	return func(d *Downloader) {
		d.RequestOptions = append(d.RequestOptions, opts...)
	}
}
func NewDownloader(c client.ConfigProvider, options ...func(*Downloader)) *Downloader {
	return newDownloader(s3.New(c), options...)
}
func newDownloader(client s3iface.S3API, options ...func(*Downloader)) *Downloader {
	d := &Downloader{
		S3:             client,
		PartSize:       DefaultDownloadPartSize,
		Concurrency:    DefaultDownloadConcurrency,
		BufferProvider: defaultDownloadBufferProvider(),
	}
	for _, option := range options {
		option(d)
	}
	return d
}
func NewDownloaderWithClient(svc s3iface.S3API, options ...func(*Downloader)) *Downloader {
	return newDownloader(svc, options...)
}
type maxRetrier interface {
	MaxRetries() int
}
func (d Downloader) Download(w io.WriterAt, input *s3.GetObjectInput, options ...func(*Downloader)) (n int64, err error) {
	return d.DownloadWithContext(aws.BackgroundContext(), w, input, options...)
}