// upload will perform a multipart upload using the firstPart buffer containing // the first chunk of data. func (u *multiuploader) upload(firstPart []byte) (*UploadOutput, error) { params := &s3.CreateMultipartUploadInput{} awsutil.Copy(params, u.in) // Create the multipart resp, err := u.s.CreateMultipartUpload(params) if err != nil { return nil, err } u.uploadID = *resp.UploadID // Create the workers ch := make(chan chunk, u.opts.Concurrency) for i := 0; i < u.opts.Concurrency; i++ { u.wg.Add(1) go u.readChunk(ch) } // Send part 1 to the workers var num int64 = 1 ch <- chunk{buf: firstPart, num: num} // Read and queue the rest of the parts for u.geterr() == nil { num++ packet := make([]byte, u.opts.PartSize) n, err := io.ReadFull(u.in.Body, packet) ch <- chunk{buf: packet[0:n], num: num} if err != nil { if err != io.EOF && err != io.ErrUnexpectedEOF { u.seterr(apierr.New("ReadRequestBody", "read multipart upload data failed", err)) } break } } // Close the channel, wait for workers, and complete upload close(ch) u.wg.Wait() complete := u.complete() if err := u.geterr(); err != nil { return nil, &multiUploadError{ BaseError: apierr.New("MultipartUpload", "upload multipart failed", err), uploadID: u.uploadID, } } return &UploadOutput{ Location: *complete.Location, UploadID: u.uploadID, }, nil }
// singlePart contains upload logic for uploading a single chunk via // a regular PutObject request. Multipart requests require at least two // parts, or at least 5MB of data. func (u *uploader) singlePart(part []byte) (*UploadOutput, error) { params := &s3.PutObjectInput{} awsutil.Copy(params, u.in) params.Body = bytes.NewReader(part) req, _ := u.s.PutObjectRequest(params) if err := req.Send(); err != nil { return nil, err } url := req.HTTPRequest.URL.String() return &UploadOutput{Location: url}, nil }