func (d *BatchDelete) Delete(ctx aws.Context, iter BatchDeleteIterator) error {
	var errs []Error
	objects := []BatchDeleteObject{}
	var input *s3.DeleteObjectsInput
	for iter.Next() {
		o := iter.DeleteObject()
		if input == nil {
			input = initDeleteObjectsInput(o.Object)
		}
		parity := hasParity(input, o)
		if parity {
			input.Delete.Objects = append(input.Delete.Objects, &s3.ObjectIdentifier{
				Key:       o.Object.Key,
				VersionId: o.Object.VersionId,
			})
			objects = append(objects, o)
		}
		if len(input.Delete.Objects) == d.BatchSize || !parity {
			if err := deleteBatch(ctx, d, input, objects); err != nil {
				errs = append(errs, err...)
			}
			objects = objects[:0]
			input = nil
			if !parity {
				objects = append(objects, o)
				input = initDeleteObjectsInput(o.Object)
				input.Delete.Objects = append(input.Delete.Objects, &s3.ObjectIdentifier{
					Key:       o.Object.Key,
					VersionId: o.Object.VersionId,
				})
			}
		}
	}
	if iter.Err() != nil {
		errs = append(errs, newError(iter.Err(), nil, nil))
	}
	if input != nil && len(input.Delete.Objects) > 0 {
		if err := deleteBatch(ctx, d, input, objects); err != nil {
			errs = append(errs, err...)
		}
	}
	if len(errs) > 0 {
		return NewBatchError("BatchedDeleteIncomplete", "some objects have failed to be deleted.", errs)
	}
	return nil
}
Delete will use the iterator to queue up objects that need to be deleted.
Once the batch size is met, this will call the deleteBatch function.
package s3manager
import (
	"bytes"
	"fmt"
	"io"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"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 (
	DefaultBatchSize = 100
)
type BatchError struct {
	Errors  Errors
	code    string
	message string
}
type Errors []Error
func (errs Errors) Error() string {
	buf := bytes.NewBuffer(nil)
	for i, err := range errs {
		buf.WriteString(err.Error())
		if i+1 < len(errs) {
			buf.WriteString("\n")
		}
	}
	return buf.String()
}
type Error struct {
	OrigErr error
	Bucket  *string
	Key     *string
}
func newError(err error, bucket, key *string) Error {
	return Error{
		err,
		bucket,
		key,
	}
}
func (err *Error) Error() string {
	origErr := ""
	if err.OrigErr != nil {
		origErr = ":\n" + err.OrigErr.Error()
	}
	return fmt.Sprintf("failed to perform batch operation on %q to %q%s",
		aws.StringValue(err.Key),
		aws.StringValue(err.Bucket),
		origErr,
	)
}
func NewBatchError(code, message string, err []Error) awserr.Error {
	return &BatchError{
		Errors:  err,
		code:    code,
		message: message,
	}
}
func (err *BatchError) Code() string {
	return err.code
}
func (err *BatchError) Message() string {
	return err.message
}
func (err *BatchError) Error() string {
	return awserr.SprintError(err.Code(), err.Message(), "", err.Errors)
}
func (err *BatchError) OrigErr() error {
	return err.Errors
}
type BatchDeleteIterator interface {
	Next() bool
	Err() error
	DeleteObject() BatchDeleteObject
}
type DeleteListIterator struct {
	Bucket    *string
	Paginator request.Pagination
	objects   []*s3.Object
}
func NewDeleteListIterator(svc s3iface.S3API, input *s3.ListObjectsInput, opts ...func(*DeleteListIterator)) BatchDeleteIterator {
	iter := &DeleteListIterator{
		Bucket: input.Bucket,
		Paginator: request.Pagination{
			NewRequest: func() (*request.Request, error) {
				var inCpy *s3.ListObjectsInput
				if input != nil {
					tmp := *input
					inCpy = &tmp
				}
				req, _ := svc.ListObjectsRequest(inCpy)
				return req, nil
			},
		},
	}
	for _, opt := range opts {
		opt(iter)
	}
	return iter
}
func (iter *DeleteListIterator) Next() bool {
	if len(iter.objects) > 0 {
		iter.objects = iter.objects[1:]
	}
	if len(iter.objects) == 0 && iter.Paginator.Next() {
		iter.objects = iter.Paginator.Page().(*s3.ListObjectsOutput).Contents
	}
	return len(iter.objects) > 0
}
func (iter *DeleteListIterator) Err() error {
	return iter.Paginator.Err()
}
func (iter *DeleteListIterator) DeleteObject() BatchDeleteObject {
	return BatchDeleteObject{
		Object: &s3.DeleteObjectInput{
			Bucket: iter.Bucket,
			Key:    iter.objects[0].Key,
		},
	}
}
type BatchDelete struct {
	Client    s3iface.S3API
	BatchSize int
}
func NewBatchDeleteWithClient(client s3iface.S3API, options ...func(*BatchDelete)) *BatchDelete {
	svc := &BatchDelete{
		Client:    client,
		BatchSize: DefaultBatchSize,
	}
	for _, opt := range options {
		opt(svc)
	}
	return svc
}
func NewBatchDelete(c client.ConfigProvider, options ...func(*BatchDelete)) *BatchDelete {
	client := s3.New(c)
	return NewBatchDeleteWithClient(client, options...)
}
type BatchDeleteObject struct {
	Object *s3.DeleteObjectInput
	After func() error
}
type DeleteObjectsIterator struct {
	Objects []BatchDeleteObject
	index   int
	inc     bool
}
func (iter *DeleteObjectsIterator) Next() bool {
	if iter.inc {
		iter.index++
	} else {
		iter.inc = true
	}
	return iter.index < len(iter.Objects)
}
func (iter *DeleteObjectsIterator) Err() error {
	return nil
}
func (iter *DeleteObjectsIterator) DeleteObject() BatchDeleteObject {
	object := iter.Objects[iter.index]
	return object
}