/* * MIT License * * Copyright (c) 2020 Nicolas JUHEL * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ package multipart import ( "fmt" "io" "math" "sync" sdksss "github.com/aws/aws-sdk-go-v2/service/s3" libctx "github.com/nabbar/golib/context" libsiz "github.com/nabbar/golib/size" ) const ( DefaultPartSize = 5 * libsiz.SizeMega MaxPartSize = 5 * libsiz.SizeGiga MaxObjectSize = 5 * libsiz.SizeTera MaxNumberPart int32 = 10000 ) type FuncClientS3 func() *sdksss.Client type MultiPart interface { io.WriteCloser RegisterContext(fct libctx.FuncContext) RegisterClientS3(fct FuncClientS3) RegisterMultipartID(id string) RegisterWorkingFile(file string, truncate bool) error RegisterFuncOnPushPart(fct func(eTag string, e error)) RegisterFuncOnAbort(fct func(nPart int, obj string, e error)) RegisterFuncOnComplete(fct func(nPart int, obj string, e error)) StartMPU() error StopMPU(abort bool) error AddPart(r io.Reader) (n int64, e error) SendPart() error CurrentSizePart() int64 AddToPart(p []byte) (n int, e error) RegisterPart(etag string) IsStarted() bool Counter() int32 CounterLeft() int32 } func New(partSize libsiz.Size, object string, bucket string) MultiPart { return &mpu{ m: sync.RWMutex{}, c: nil, s: partSize, i: "", o: object, b: bucket, n: 0, } } func GetOptimalPartSize(objectSize, partSize libsiz.Size) (libsiz.Size, error) { var ( lim = math.MaxFloat64 nbr int64 prt int64 obj int64 ) if partSize <= DefaultPartSize { prt = DefaultPartSize.Int64() } else { prt = partSize.Int64() } obj = objectSize.Int64() if obj > (int64(MaxNumberPart) * MaxPartSize.Int64()) { return 0, fmt.Errorf("object size need exceed the maximum number of part with the maximum size of part") } else if objectSize > MaxObjectSize { return 0, fmt.Errorf("object size is over allowed maximum size of object") } else if uint64(obj) > uint64(lim) || uint64(prt) > uint64(lim) || uint64(obj/prt) > uint64(lim) { return GetOptimalPartSize(objectSize, libsiz.SizeFromInt64(prt*2)) } else if nbr = int64(math.Ceil(float64(obj) / float64(prt))); nbr > int64(MaxNumberPart) { return GetOptimalPartSize(objectSize, libsiz.SizeFromInt64(prt*2)) } else if prt > MaxPartSize.Int64() { return GetOptimalPartSize(objectSize, libsiz.SizeFromInt64((prt/4)*3)) } return libsiz.SizeFromInt64(prt), nil }