ComposeObject - creates an object using server-side copying of existing objects. It takes a list of source objects (with optional offsets) and concatenates them into a new object using only server-side copying operations. Optionally takes progress reader hook for applications to look at current prog
(ctx context.Context, dst CopyDestOptions, srcs ...CopySrcOptions)
| 415 | // operations. Optionally takes progress reader hook for applications to |
| 416 | // look at current progress. |
| 417 | func (c *Client) ComposeObject(ctx context.Context, dst CopyDestOptions, srcs ...CopySrcOptions) (UploadInfo, error) { |
| 418 | if len(srcs) < 1 || len(srcs) > maxPartsCount { |
| 419 | return UploadInfo{}, errInvalidArgument("There must be as least one and up to 10000 source objects.") |
| 420 | } |
| 421 | |
| 422 | for _, src := range srcs { |
| 423 | if err := src.validate(); err != nil { |
| 424 | return UploadInfo{}, err |
| 425 | } |
| 426 | } |
| 427 | |
| 428 | if err := dst.validate(); err != nil { |
| 429 | return UploadInfo{}, err |
| 430 | } |
| 431 | |
| 432 | srcObjectInfos := make([]ObjectInfo, len(srcs)) |
| 433 | srcObjectSizes := make([]int64, len(srcs)) |
| 434 | var totalSize, totalParts int64 |
| 435 | var err error |
| 436 | for i, src := range srcs { |
| 437 | opts := StatObjectOptions{ServerSideEncryption: encrypt.SSE(src.Encryption), VersionID: src.VersionID} |
| 438 | srcObjectInfos[i], err = c.StatObject(context.Background(), src.Bucket, src.Object, opts) |
| 439 | if err != nil { |
| 440 | return UploadInfo{}, err |
| 441 | } |
| 442 | |
| 443 | srcCopySize := srcObjectInfos[i].Size |
| 444 | // Check if a segment is specified, and if so, is the |
| 445 | // segment within object bounds? |
| 446 | if src.MatchRange { |
| 447 | // Since range is specified, |
| 448 | // 0 <= src.start <= src.end |
| 449 | // so only invalid case to check is: |
| 450 | if src.End >= srcCopySize || src.Start < 0 { |
| 451 | return UploadInfo{}, errInvalidArgument( |
| 452 | fmt.Sprintf("CopySrcOptions %d has invalid segment-to-copy [%d, %d] (size is %d)", |
| 453 | i, src.Start, src.End, srcCopySize)) |
| 454 | } |
| 455 | srcCopySize = src.End - src.Start + 1 |
| 456 | } |
| 457 | |
| 458 | // Only the last source may be less than `absMinPartSize` |
| 459 | if srcCopySize < absMinPartSize && i < len(srcs)-1 { |
| 460 | return UploadInfo{}, errInvalidArgument( |
| 461 | fmt.Sprintf("CopySrcOptions %d is too small (%d) and it is not the last part", i, srcCopySize)) |
| 462 | } |
| 463 | |
| 464 | // Is data to copy too large? |
| 465 | totalSize += srcCopySize |
| 466 | if totalSize > maxObjectSize { |
| 467 | return UploadInfo{}, errInvalidArgument(fmt.Sprintf("Cannot compose an object of size %d (> 5GiB * 10000)", totalSize)) |
| 468 | } |
| 469 | |
| 470 | // record source size |
| 471 | srcObjectSizes[i] = srcCopySize |
| 472 | |
| 473 | // calculate parts needed for current source |
| 474 | totalParts += partsRequired(srcCopySize, int64(dst.PartSize)) |
no test coverage detected