func (a *AWSS3Backend) Delete(ctx context.Context, key string) error {
	_, err := a.client.DeleteObjectWithContext(ctx, &s3.DeleteObjectInput{
		Bucket: aws.String(a.bucketName),
		Key:    aws.String(a.prefix + key),
	})
	return err
}
Delete will delete the given object from the configured bucket
package backends
import (
	"context"
	"crypto/md5"  
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/request"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3iface"
	"github.com/aws/aws-sdk-go/service/s3/s3manager"
	"github.com/aws/aws-sdk-go/service/s3/s3manager/s3manageriface"
	"github.com/someone1/zfsbackup-go/files"
	"github.com/someone1/zfsbackup-go/log"
)
const AWSS3BackendPrefix = "s3"
type AWSS3Backend struct {
	conf       *BackendConfig
	mutex      sync.Mutex
	client     s3iface.S3API
	uploader   s3manageriface.UploaderAPI
	prefix     string
	bucketName string
}
type logger struct{}
func (l logger) Log(args ...interface{}) {
	log.AppLogger.Debugf("s3 backend:", args...)
}
type withS3Client struct{ client s3iface.S3API }
func (w withS3Client) Apply(b Backend) {
	if v, ok := b.(*AWSS3Backend); ok {
		v.client = w.client
	}
}
func WithS3Client(c s3iface.S3API) Option {
	return withS3Client{c}
}
type withS3Uploader struct{ uploader s3manageriface.UploaderAPI }
func (w withS3Uploader) Apply(b Backend) {
	if v, ok := b.(*AWSS3Backend); ok {
		v.uploader = w.uploader
	}
}
func WithS3Uploader(c s3manageriface.UploaderAPI) Option {
	return withS3Uploader{c}
}
func (a *AWSS3Backend) Init(ctx context.Context, conf *BackendConfig, opts ...Option) error {
	a.conf = conf
	cleanPrefix := strings.TrimPrefix(a.conf.TargetURI, AWSS3BackendPrefix+"://")
	if cleanPrefix == a.conf.TargetURI {
		return ErrInvalidURI
	}
	uriParts := strings.Split(cleanPrefix, "/")
	a.bucketName = uriParts[0]
	if len(uriParts) > 1 {
		a.prefix = strings.Join(uriParts[1:], "/")
	}
	for _, opt := range opts {
		opt.Apply(a)
	}
	if a.client == nil {
		awsconf := aws.NewConfig().
			WithS3ForcePathStyle(true).
			WithEndpoint(os.Getenv("AWS_S3_CUSTOM_ENDPOINT"))
		if enableDebug, _ := strconv.ParseBool(os.Getenv("AWS_S3_ENABLE_DEBUG")); enableDebug {
			awsconf = awsconf.WithLogger(logger{}).
				WithLogLevel(aws.LogDebugWithRequestRetries | aws.LogDebugWithRequestErrors)
		}
		sess, err := session.NewSession(awsconf)
		if err != nil {
			return err
		}
		a.client = s3.New(sess)
	}
	if a.uploader == nil {
		a.uploader = s3manager.NewUploaderWithClient(a.client, func(u *s3manager.Uploader) {
			u.Concurrency = conf.MaxParallelUploads
		}, func(u *s3manager.Uploader) {
			u.PartSize = int64(conf.UploadChunkSize)
		})
	}
	listReq := &s3.ListObjectsV2Input{
		Bucket:  aws.String(a.bucketName),
		MaxKeys: aws.Int64(0),
	}
	_, err := a.client.ListObjectsV2WithContext(ctx, listReq)
	return err
}
func withContentMD5Header(md5sum string) request.Option {
	return func(ro *request.Request) {
		if md5sum != "" {
			ro.Handlers.Build.PushBack(func(r *request.Request) {
				r.HTTPRequest.Header.Set("Content-MD5", md5sum)
			})
		}
	}
}
func withRequestLimiter(buffer chan bool) request.Option {
	return func(ro *request.Request) {
		ro.Handlers.Send.PushFront(func(r *request.Request) {
			buffer <- true
		})
		ro.Handlers.Send.PushBack(func(r *request.Request) {
			<-buffer
		})
	}
}
func withComputeMD5HashHandler(ro *request.Request) {
	ro.Handlers.Build.PushBack(func(r *request.Request) {
		reader := r.GetBody()
		if reader == nil {
			return
		}
		md5Raw := md5.New()
		_, err := io.Copy(md5Raw, reader)
		if err != nil {
			r.Error = err
			return
		}
		_, r.Error = reader.Seek(0, io.SeekStart)
		b64md5 := base64.StdEncoding.EncodeToString(md5Raw.Sum(nil))
		r.HTTPRequest.Header.Set("Content-MD5", b64md5)
	})
}
type reader struct {
	r io.Reader
}
func (r *reader) Read(p []byte) (int, error) {
	return r.r.Read(p)
}
func (a *AWSS3Backend) Upload(ctx context.Context, vol *files.VolumeInfo) error {
	a.mutex.Lock()
	defer a.mutex.Unlock()
	key := a.prefix + vol.ObjectName
	var options []request.Option
	options = append(options, withRequestLimiter(a.conf.MaxParallelUploadBuffer))
	var r io.Reader
	if !vol.IsUsingPipe() {
		r = vol
		if vol.Size < uint64(s3manager.MinUploadPartSize) {
			md5Raw, merr := hex.DecodeString(vol.MD5Sum)
			if merr != nil {
				return merr
			}
			b64md5 := base64.StdEncoding.EncodeToString(md5Raw)
			options = append(options, withContentMD5Header(b64md5))
		} else {
			options = append(options, withComputeMD5HashHandler)
		}
	} else {
		r = &reader{vol}  
	}
	_, err := a.uploader.UploadWithContext(ctx, &s3manager.UploadInput{
		Bucket: aws.String(a.bucketName),
		Key:    aws.String(key),
		Body:   r,
	}, s3manager.WithUploaderRequestOptions(options...))
	if err != nil {
		log.AppLogger.Debugf("s3 backend: Error while uploading volume %s - %v", vol.ObjectName, err)
	}
	return err
}