func (s *S3Storage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) (err error) {
	key := fmt.Sprintf("%s/%s", token, filename)
	s.logger.Printf("Uploading file %s to S3 Bucket", filename)
	var concurrency int
	if !s.noMultipart {
		concurrency = 20
	} else {
		concurrency = 1
	}
	uploader := s3manager.NewUploader(s.session, func(u *s3manager.Uploader) {
		u.Concurrency = concurrency  
		u.LeavePartsOnError = false
	})
	var expire *time.Time
	if s.purgeDays.Hours() > 0 {
		expire = aws.Time(time.Now().Add(s.purgeDays))
	}
	_, err = uploader.Upload(&s3manager.UploadInput{
		Bucket:  aws.String(s.bucket),
		Key:     aws.String(key),
		Body:    reader,
		Expires: expire,
	})
	return
}
Put saves a file on storage
package server
import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3manager"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/drive/v3"
	"google.golang.org/api/googleapi"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"
	"storj.io/common/storj"
	"storj.io/uplink"
)
type Storage interface {
	Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error)
	Head(token string, filename string) (contentLength uint64, err error)
	Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error
	Delete(token string, filename string) error
	IsNotExist(err error) bool
	Purge(days time.Duration) error
	Type() string
}
type LocalStorage struct {
	Storage
	basedir string
	logger  *log.Logger
}
func NewLocalStorage(basedir string, logger *log.Logger) (*LocalStorage, error) {
	return &LocalStorage{basedir: basedir, logger: logger}, nil
}
func (s *LocalStorage) Type() string {
	return "local"
}
func (s *LocalStorage) Head(token string, filename string) (contentLength uint64, err error) {
	path := filepath.Join(s.basedir, token, filename)
	var fi os.FileInfo
	if fi, err = os.Lstat(path); err != nil {
		return
	}
	contentLength = uint64(fi.Size())
	return
}
func (s *LocalStorage) Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) {
	path := filepath.Join(s.basedir, token, filename)
	if reader, err = os.Open(path); err != nil {
		return
	}
	var fi os.FileInfo
	if fi, err = os.Lstat(path); err != nil {
		return
	}
	contentLength = uint64(fi.Size())
	return
}
func (s *LocalStorage) Delete(token string, filename string) (err error) {
	metadata := filepath.Join(s.basedir, token, fmt.Sprintf("%s.metadata", filename))
	os.Remove(metadata)
	path := filepath.Join(s.basedir, token, filename)
	err = os.Remove(path)
	return
}
func (s *LocalStorage) Purge(days time.Duration) (err error) {
	err = filepath.Walk(s.basedir,
		func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if info.IsDir() {
				return nil
			}
			if info.ModTime().Before(time.Now().Add(-1 * days)) {
				err = os.Remove(path)
				return err
			}
			return nil
		})
	return
}
func (s *LocalStorage) IsNotExist(err error) bool {
	if err == nil {
		return false
	}
	return os.IsNotExist(err)
}
func (s *LocalStorage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error {
	var f io.WriteCloser
	var err error
	path := filepath.Join(s.basedir, token)
	if err = os.MkdirAll(path, 0700); err != nil && !os.IsExist(err) {
		return err
	}
	if f, err = os.OpenFile(filepath.Join(path, filename), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600); err != nil {
		return err
	}
	defer f.Close()
	if _, err = io.Copy(f, reader); err != nil {
		return err
	}
	return nil
}
type S3Storage struct {
	Storage
	bucket      string
	session     *session.Session
	s3          *s3.S3
	logger      *log.Logger
	purgeDays   time.Duration
	noMultipart bool
}
func NewS3Storage(accessKey, secretKey, bucketName string, purgeDays int, region, endpoint string, disableMultipart bool, forcePathStyle bool, logger *log.Logger) (*S3Storage, error) {
	sess := getAwsSession(accessKey, secretKey, region, endpoint, forcePathStyle)
	return &S3Storage{
		bucket:      bucketName,
		s3:          s3.New(sess),
		session:     sess,
		logger:      logger,
		noMultipart: disableMultipart,
		purgeDays:   time.Duration(purgeDays*24) * time.Hour,
	}, nil
}
func (s *S3Storage) Type() string {
	return "s3"
}
func (s *S3Storage) Head(token string, filename string) (contentLength uint64, err error) {
	key := fmt.Sprintf("%s/%s", token, filename)
	headRequest := &s3.HeadObjectInput{
		Bucket: aws.String(s.bucket),
		Key:    aws.String(key),
	}
	response, err := s.s3.HeadObject(headRequest)
	if err != nil {
		return
	}
	if response.ContentLength != nil {
		contentLength = uint64(*response.ContentLength)
	}
	return
}
func (s *S3Storage) Purge(days time.Duration) (err error) {
	return nil
}
func (s *S3Storage) IsNotExist(err error) bool {
	if err == nil {
		return false
	}
	if aerr, ok := err.(awserr.Error); ok {
		switch aerr.Code() {
		case s3.ErrCodeNoSuchKey:
			return true
		}
	}
	return false
}
func (s *S3Storage) Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) {
	key := fmt.Sprintf("%s/%s", token, filename)
	getRequest := &s3.GetObjectInput{
		Bucket: aws.String(s.bucket),
		Key:    aws.String(key),
	}
	response, err := s.s3.GetObject(getRequest)
	if err != nil {
		return
	}
	if response.ContentLength != nil {
		contentLength = uint64(*response.ContentLength)
	}
	reader = response.Body
	return
}
func (s *S3Storage) Delete(token string, filename string) (err error) {
	metadata := fmt.Sprintf("%s/%s.metadata", token, filename)
	deleteRequest := &s3.DeleteObjectInput{
		Bucket: aws.String(s.bucket),
		Key:    aws.String(metadata),
	}
	_, err = s.s3.DeleteObject(deleteRequest)
	if err != nil {
		return
	}
	key := fmt.Sprintf("%s/%s", token, filename)
	deleteRequest = &s3.DeleteObjectInput{
		Bucket: aws.String(s.bucket),
		Key:    aws.String(key),
	}
	_, err = s.s3.DeleteObject(deleteRequest)
	return
}