vendor: update all dependencies to latest versions
This commit is contained in:
+106
@@ -0,0 +1,106 @@
|
||||
package upload
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// QingStor has a max upload parts limit to 10000.
|
||||
maxUploadParts = 10000
|
||||
// We read from stream for read 1024B.
|
||||
segmentSize = 1024
|
||||
)
|
||||
|
||||
// chunk provides a struct to read file
|
||||
type chunk struct {
|
||||
fd io.Reader
|
||||
cur int64
|
||||
size int64
|
||||
partSize int
|
||||
}
|
||||
|
||||
// newChunk creates a FileChunk struct
|
||||
func newChunk(fd io.Reader, partSize int) *chunk {
|
||||
f := &chunk{
|
||||
fd: fd,
|
||||
partSize: partSize,
|
||||
}
|
||||
f.initSize()
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// nextPart reads the next part of the file
|
||||
func (f *chunk) nextPart() (io.ReadSeeker, error) {
|
||||
type readerAtSeeker interface {
|
||||
io.ReaderAt
|
||||
io.ReadSeeker
|
||||
}
|
||||
switch r := f.fd.(type) {
|
||||
case readerAtSeeker:
|
||||
var sectionSize int64
|
||||
var err error
|
||||
leftSize := f.size - f.cur
|
||||
if leftSize >= int64(f.partSize) {
|
||||
sectionSize = int64(f.partSize)
|
||||
} else if leftSize > 0 {
|
||||
sectionSize = f.size - f.cur
|
||||
} else {
|
||||
err = io.EOF
|
||||
}
|
||||
seekReader := io.NewSectionReader(r, f.cur, sectionSize)
|
||||
f.cur += sectionSize
|
||||
return seekReader, err
|
||||
case io.Reader:
|
||||
buf := make([]byte, segmentSize)
|
||||
var n, lenBuf int
|
||||
var err error
|
||||
var chunk []byte
|
||||
for {
|
||||
n, _ = r.Read(buf)
|
||||
if n == 0 {
|
||||
if lenBuf == 0 {
|
||||
err = io.EOF
|
||||
}
|
||||
break
|
||||
}
|
||||
lenBuf = lenBuf + n
|
||||
chunk = append(chunk, buf...)
|
||||
if lenBuf == f.partSize {
|
||||
break
|
||||
}
|
||||
}
|
||||
partBody := bytes.NewReader(chunk[:lenBuf])
|
||||
return partBody, err
|
||||
default:
|
||||
return nil, errors.New("file does not support read")
|
||||
}
|
||||
}
|
||||
|
||||
// initSize tries to detect the total stream size, setting u.size. If
|
||||
// the size is not known, size is set to -1.
|
||||
func (f *chunk) initSize() {
|
||||
f.size = -1
|
||||
|
||||
switch r := f.fd.(type) {
|
||||
case io.Seeker:
|
||||
pos, _ := r.Seek(0, 1)
|
||||
defer r.Seek(pos, 0)
|
||||
|
||||
n, err := r.Seek(0, 2)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
f.size = n
|
||||
|
||||
// Try to adjust partSize if it is too small and account for
|
||||
// integer division truncation.
|
||||
if f.size/int64(f.partSize) >= int64(maxUploadParts) {
|
||||
// Add one to the part size to account for remainders
|
||||
// during the size calculation. e.g odd number of bytes.
|
||||
f.partSize = int(f.size/int64(maxUploadParts)) + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package upload
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var partSize = 5 * 1024
|
||||
|
||||
//Test_newFileChunk is the test function for New
|
||||
func Test_newFileChunk(t *testing.T) {
|
||||
setup()
|
||||
|
||||
fd, _ := os.Open("test_file")
|
||||
defer fd.Close()
|
||||
fr := newChunk(fd, partSize)
|
||||
if fr.size != 512000 {
|
||||
t.Fatalf("expected 512000, got %d", fr.size)
|
||||
}
|
||||
|
||||
tearDown()
|
||||
}
|
||||
|
||||
// Test_nextPart is the test function for nextSeekablePart
|
||||
func Test_nextPart(t *testing.T) {
|
||||
setup()
|
||||
|
||||
fd, _ := os.Open("test_file")
|
||||
defer fd.Close()
|
||||
fr := newChunk(fd, partSize)
|
||||
partBody, err := fr.nextPart()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
temp := make([]byte, 6000)
|
||||
n, _ := partBody.Read(temp)
|
||||
if n != partSize {
|
||||
t.Fatalf("expected 5120, got %d", len(temp))
|
||||
}
|
||||
|
||||
tearDown()
|
||||
}
|
||||
|
||||
func setup() {
|
||||
exec.Command("dd", "if=/dev/zero", "of=test_file", "bs=1024", "count=500").Output()
|
||||
}
|
||||
|
||||
func tearDown() {
|
||||
exec.Command("rm", "", "test_file").Output()
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
package upload
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/yunify/qingstor-sdk-go/logger"
|
||||
"github.com/yunify/qingstor-sdk-go/service"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Uploader struct provides a struct to upload
|
||||
type Uploader struct {
|
||||
bucket *service.Bucket
|
||||
partSize int
|
||||
}
|
||||
|
||||
const smallestPartSize int = 1024 * 1024 * 4
|
||||
|
||||
//Init creates a uploader struct
|
||||
func Init(bucket *service.Bucket, partSize int) *Uploader {
|
||||
return &Uploader{
|
||||
bucket: bucket,
|
||||
partSize: partSize,
|
||||
}
|
||||
}
|
||||
|
||||
// Upload uploads multi parts of large object
|
||||
func (u *Uploader) Upload(fd io.Reader, objectKey string) error {
|
||||
if u.partSize < smallestPartSize {
|
||||
logger.Errorf("Part size error")
|
||||
return errors.New("the part size is too small")
|
||||
}
|
||||
|
||||
uploadID, err := u.init(objectKey)
|
||||
if err != nil {
|
||||
logger.Errorf("Init multipart upload error" + err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
partNumbers, err := u.upload(fd, uploadID, objectKey)
|
||||
if err != nil {
|
||||
logger.Errorf("Upload multipart error" + err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
err = u.complete(objectKey, uploadID, partNumbers)
|
||||
if err != nil {
|
||||
logger.Errorf("Complete upload error" + err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Uploader) init(objectKey string) (*string, error) {
|
||||
output, err := u.bucket.InitiateMultipartUpload(
|
||||
objectKey,
|
||||
&service.InitiateMultipartUploadInput{},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return output.UploadID, nil
|
||||
}
|
||||
|
||||
func (u *Uploader) upload(fd io.Reader, uploadID *string, objectKey string) ([]*service.ObjectPartType, error) {
|
||||
var partCnt int
|
||||
partNumbers := []*service.ObjectPartType{}
|
||||
fileReader := newChunk(fd, u.partSize)
|
||||
for {
|
||||
partBody, err := fileReader.nextPart()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
logger.Errorf("Get next part failed for %v", err)
|
||||
return nil, err
|
||||
}
|
||||
_, err = u.bucket.UploadMultipart(
|
||||
objectKey,
|
||||
&service.UploadMultipartInput{
|
||||
UploadID: uploadID,
|
||||
PartNumber: &partCnt,
|
||||
Body: partBody,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
logger.Errorf("Upload multipart failed for %v", err)
|
||||
return nil, err
|
||||
}
|
||||
partNumbers = append(partNumbers, &service.ObjectPartType{
|
||||
PartNumber: service.Int(partCnt - 0),
|
||||
})
|
||||
partCnt++
|
||||
}
|
||||
return partNumbers, nil
|
||||
}
|
||||
|
||||
func (u *Uploader) complete(objectKey string, uploadID *string, partNumbers []*service.ObjectPartType) error {
|
||||
_, err := u.bucket.CompleteMultipartUpload(
|
||||
objectKey,
|
||||
&service.CompleteMultipartUploadInput{
|
||||
UploadID: uploadID,
|
||||
ObjectParts: partNumbers,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user