func (d *downloader) downloadChunk(chunk dlchunk) error {
	in := &s3.GetObjectInput{}
	awsutil.Copy(in, d.in)
	in.Range = aws.String(chunk.ByteRange())
	var n int64
	var err error
	for retry := 0; retry <= d.partBodyMaxRetries; retry++ {
		var resp *s3.GetObjectOutput
		resp, err = d.cfg.S3.GetObjectWithContext(d.ctx, in, d.cfg.RequestOptions...)
		if err != nil {
			return err
		}
		d.setTotalBytes(resp)  
		n, err = io.Copy(&chunk, resp.Body)
		resp.Body.Close()
		if err == nil {
			break
		}
		chunk.cur = 0
		logMessage(d.cfg.S3, aws.LogDebugWithRequestRetries,
			fmt.Sprintf("DEBUG: object part body download interrupted %s, err, %v, retrying attempt %d",
				aws.StringValue(in.Key), err, retry))
	}
	d.incrWritten(n)
	return err
}
downloadChunk downloads the chunk from s3
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 Downloader struct {
	PartSize int64
	Concurrency int
	S3 s3iface.S3API
	RequestOptions []request.Option
}
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 {
	d := &Downloader{
		S3:          s3.New(c),
		PartSize:    DefaultDownloadPartSize,
		Concurrency: DefaultDownloadConcurrency,
	}
	for _, option := range options {
		option(d)
	}
	return d
}
func NewDownloaderWithClient(svc s3iface.S3API, options ...func(*Downloader)) *Downloader {
	d := &Downloader{
		S3:          svc,
		PartSize:    DefaultDownloadPartSize,
		Concurrency: DefaultDownloadConcurrency,
	}
	for _, option := range options {
		option(d)
	}
	return d
}
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...)
}
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()
}
func (d Downloader) DownloadWithIterator(ctx aws.Context, iter BatchDownloadIterator, opts ...func(*Downloader)) error {
	var errs []Error
	for iter.Next() {
		object := iter.DownloadObject()
		if _, err := d.DownloadWithContext(ctx, object.Writer, object.Object, opts...); err != nil {
			errs = append(errs, newError(err, object.Object.Bucket, object.Object.Key))
		}
		if object.After == nil {
			continue
		}
		if err := object.After(); err != nil {
			errs = append(errs, newError(err, object.Object.Bucket, object.Object.Key))
		}
	}
	if len(errs) > 0 {
		return NewBatchError("BatchedDownloadIncomplete", "some objects have failed to download.", errs)
	}
	return nil
}
type downloader struct {
	ctx aws.Context
	cfg Downloader
	in *s3.GetObjectInput
	w  io.WriterAt
	wg sync.WaitGroup
	m  sync.Mutex
	pos        int64
	totalBytes int64
	written    int64
	err        error
	partBodyMaxRetries int
}
func (d *downloader) download() (n int64, err error) {
	if rng := aws.StringValue(d.in.Range); len(rng) > 0 {
		d.downloadRange(rng)
		return d.written, d.err
	}
	d.getChunk()
	if total := d.getTotalBytes(); total >= 0 {
		ch := make(chan dlchunk, d.cfg.Concurrency)
		for i := 0; i < d.cfg.Concurrency; i++ {
			d.wg.Add(1)
			go d.downloadPart(ch)
		}
		for d.getErr() == nil {
			if d.pos >= total {
				break  
			}
			ch <- dlchunk{w: d.w, start: d.pos, size: d.cfg.PartSize}
			d.pos += d.cfg.PartSize
		}
		close(ch)
		d.wg.Wait()
	} else {
		for d.err == nil {
			d.getChunk()
		}
		e, ok := d.err.(awserr.RequestFailure)
		if ok && e.StatusCode() == http.StatusRequestedRangeNotSatisfiable {
			d.err = nil
		}
	}
	return d.written, d.err
}
func (d *downloader) downloadPart(ch chan dlchunk) {
	defer d.wg.Done()
	for {
		chunk, ok := <-ch
		if !ok {
			break
		}
		if d.getErr() != nil {
			continue
		}
		if err := d.downloadChunk(chunk); err != nil {
			d.setErr(err)
		}
	}
}
func (d *downloader) getChunk() {
	if d.getErr() != nil {
		return
	}
	chunk := dlchunk{w: d.w, start: d.pos, size: d.cfg.PartSize}
	d.pos += d.cfg.PartSize
	if err := d.downloadChunk(chunk); err != nil {
		d.setErr(err)
	}
}
func (d *downloader) downloadRange(rng string) {
	if d.getErr() != nil {
		return
	}
	chunk := dlchunk{w: d.w, start: d.pos}
	chunk.withRange = rng
	if err := d.downloadChunk(chunk); err != nil {
		d.setErr(err)
	}
	d.pos = d.written
}