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)
| 433 | // operations. Optionally takes progress reader hook for applications to |
| 434 | // look at current progress. |
| 435 | func (c *Client) ComposeObject(ctx context.Context, dst CopyDestOptions, srcs ...CopySrcOptions) (UploadInfo, error) { |
| 436 | if len(srcs) < 1 || len(srcs) > maxPartsCount { |
| 437 | return UploadInfo{}, errInvalidArgument("There must be as least one and up to 10000 source objects.") |
| 438 | } |
| 439 | |
| 440 | for _, src := range srcs { |
| 441 | if err := src.validate(); err != nil { |
| 442 | return UploadInfo{}, err |
| 443 | } |
| 444 | } |
| 445 | |
| 446 | if err := dst.validate(); err != nil { |
| 447 | return UploadInfo{}, err |
| 448 | } |
| 449 | |
| 450 | srcObjectInfos := make([]ObjectInfo, len(srcs)) |
| 451 | srcObjectSizes := make([]int64, len(srcs)) |
| 452 | var totalSize, totalParts int64 |
| 453 | var err error |
| 454 | for i, src := range srcs { |
| 455 | opts := StatObjectOptions{ServerSideEncryption: encrypt.SSE(src.Encryption), VersionID: src.VersionID} |
| 456 | srcObjectInfos[i], err = c.StatObject(context.Background(), src.Bucket, src.Object, opts) |
| 457 | if err != nil { |
| 458 | return UploadInfo{}, err |
| 459 | } |
| 460 | |
| 461 | srcCopySize := srcObjectInfos[i].Size |
| 462 | // Check if a segment is specified, and if so, is the |
| 463 | // segment within object bounds? |
| 464 | if src.MatchRange { |
| 465 | // Since range is specified, |
| 466 | // 0 <= src.start <= src.end |
| 467 | // so only invalid case to check is: |
| 468 | if src.End >= srcCopySize || src.Start < 0 { |
| 469 | return UploadInfo{}, errInvalidArgument( |
| 470 | fmt.Sprintf("CopySrcOptions %d has invalid segment-to-copy [%d, %d] (size is %d)", |
| 471 | i, src.Start, src.End, srcCopySize)) |
| 472 | } |
| 473 | srcCopySize = src.End - src.Start + 1 |
| 474 | } |
| 475 | |
| 476 | // Only the last source may be less than `absMinPartSize` |
| 477 | if srcCopySize < absMinPartSize && i < len(srcs)-1 { |
| 478 | return UploadInfo{}, errInvalidArgument( |
| 479 | fmt.Sprintf("CopySrcOptions %d is too small (%d) and it is not the last part", i, srcCopySize)) |
| 480 | } |
| 481 | |
| 482 | // Is data to copy too large? |
| 483 | totalSize += srcCopySize |
| 484 | if totalSize > maxObjectSize { |
| 485 | return UploadInfo{}, errInvalidArgument(fmt.Sprintf("Cannot compose an object of size %d (> 5GiB * 10000)", totalSize)) |
| 486 | } |
| 487 | |
| 488 | // record source size |
| 489 | srcObjectSizes[i] = srcCopySize |
| 490 | |
| 491 | // calculate parts needed for current source |
| 492 | totalParts += partsRequired(srcCopySize, int64(dst.PartSize)) |
no test coverage detected