123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- // Copyright 2016 Google Inc. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package storage
- import (
- "errors"
- "fmt"
- "cloud.google.com/go/internal/trace"
- "golang.org/x/net/context"
- raw "google.golang.org/api/storage/v1"
- )
- // CopierFrom creates a Copier that can copy src to dst.
- // You can immediately call Run on the returned Copier, or
- // you can configure it first.
- //
- // For Requester Pays buckets, the user project of dst is billed, unless it is empty,
- // in which case the user project of src is billed.
- func (dst *ObjectHandle) CopierFrom(src *ObjectHandle) *Copier {
- return &Copier{dst: dst, src: src}
- }
- // A Copier copies a source object to a destination.
- type Copier struct {
- // ObjectAttrs are optional attributes to set on the destination object.
- // Any attributes must be initialized before any calls on the Copier. Nil
- // or zero-valued attributes are ignored.
- ObjectAttrs
- // RewriteToken can be set before calling Run to resume a copy
- // operation. After Run returns a non-nil error, RewriteToken will
- // have been updated to contain the value needed to resume the copy.
- RewriteToken string
- // ProgressFunc can be used to monitor the progress of a multi-RPC copy
- // operation. If ProgressFunc is not nil and copying requires multiple
- // calls to the underlying service (see
- // https://cloud.google.com/storage/docs/json_api/v1/objects/rewrite), then
- // ProgressFunc will be invoked after each call with the number of bytes of
- // content copied so far and the total size in bytes of the source object.
- //
- // ProgressFunc is intended to make upload progress available to the
- // application. For example, the implementation of ProgressFunc may update
- // a progress bar in the application's UI, or log the result of
- // float64(copiedBytes)/float64(totalBytes).
- //
- // ProgressFunc should return quickly without blocking.
- ProgressFunc func(copiedBytes, totalBytes uint64)
- // The Cloud KMS key, in the form projects/P/locations/L/keyRings/R/cryptoKeys/K,
- // that will be used to encrypt the object. Overrides the object's KMSKeyName, if
- // any.
- //
- // Providing both a DestinationKMSKeyName and a customer-supplied encryption key
- // (via ObjectHandle.Key) on the destination object will result in an error when
- // Run is called.
- DestinationKMSKeyName string
- dst, src *ObjectHandle
- }
- // Run performs the copy.
- func (c *Copier) Run(ctx context.Context) (attrs *ObjectAttrs, err error) {
- ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Copier.Run")
- defer func() { trace.EndSpan(ctx, err) }()
- if err := c.src.validate(); err != nil {
- return nil, err
- }
- if err := c.dst.validate(); err != nil {
- return nil, err
- }
- if c.DestinationKMSKeyName != "" && c.dst.encryptionKey != nil {
- return nil, errors.New("storage: cannot use DestinationKMSKeyName with a customer-supplied encryption key")
- }
- // Convert destination attributes to raw form, omitting the bucket.
- // If the bucket is included but name or content-type aren't, the service
- // returns a 400 with "Required" as the only message. Omitting the bucket
- // does not cause any problems.
- rawObject := c.ObjectAttrs.toRawObject("")
- for {
- res, err := c.callRewrite(ctx, rawObject)
- if err != nil {
- return nil, err
- }
- if c.ProgressFunc != nil {
- c.ProgressFunc(uint64(res.TotalBytesRewritten), uint64(res.ObjectSize))
- }
- if res.Done { // Finished successfully.
- return newObject(res.Resource), nil
- }
- }
- }
- func (c *Copier) callRewrite(ctx context.Context, rawObj *raw.Object) (*raw.RewriteResponse, error) {
- call := c.dst.c.raw.Objects.Rewrite(c.src.bucket, c.src.object, c.dst.bucket, c.dst.object, rawObj)
- call.Context(ctx).Projection("full")
- if c.RewriteToken != "" {
- call.RewriteToken(c.RewriteToken)
- }
- if c.DestinationKMSKeyName != "" {
- call.DestinationKmsKeyName(c.DestinationKMSKeyName)
- }
- if err := applyConds("Copy destination", c.dst.gen, c.dst.conds, call); err != nil {
- return nil, err
- }
- if c.dst.userProject != "" {
- call.UserProject(c.dst.userProject)
- } else if c.src.userProject != "" {
- call.UserProject(c.src.userProject)
- }
- if err := applySourceConds(c.src.gen, c.src.conds, call); err != nil {
- return nil, err
- }
- if err := setEncryptionHeaders(call.Header(), c.dst.encryptionKey, false); err != nil {
- return nil, err
- }
- if err := setEncryptionHeaders(call.Header(), c.src.encryptionKey, true); err != nil {
- return nil, err
- }
- var res *raw.RewriteResponse
- var err error
- setClientHeader(call.Header())
- err = runWithRetry(ctx, func() error { res, err = call.Do(); return err })
- if err != nil {
- return nil, err
- }
- c.RewriteToken = res.RewriteToken
- return res, nil
- }
- // ComposerFrom creates a Composer that can compose srcs into dst.
- // You can immediately call Run on the returned Composer, or you can
- // configure it first.
- //
- // The encryption key for the destination object will be used to decrypt all
- // source objects and encrypt the destination object. It is an error
- // to specify an encryption key for any of the source objects.
- func (dst *ObjectHandle) ComposerFrom(srcs ...*ObjectHandle) *Composer {
- return &Composer{dst: dst, srcs: srcs}
- }
- // A Composer composes source objects into a destination object.
- //
- // For Requester Pays buckets, the user project of dst is billed.
- type Composer struct {
- // ObjectAttrs are optional attributes to set on the destination object.
- // Any attributes must be initialized before any calls on the Composer. Nil
- // or zero-valued attributes are ignored.
- ObjectAttrs
- dst *ObjectHandle
- srcs []*ObjectHandle
- }
- // Run performs the compose operation.
- func (c *Composer) Run(ctx context.Context) (attrs *ObjectAttrs, err error) {
- ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Composer.Run")
- defer func() { trace.EndSpan(ctx, err) }()
- if err := c.dst.validate(); err != nil {
- return nil, err
- }
- if len(c.srcs) == 0 {
- return nil, errors.New("storage: at least one source object must be specified")
- }
- req := &raw.ComposeRequest{}
- // Compose requires a non-empty Destination, so we always set it,
- // even if the caller-provided ObjectAttrs is the zero value.
- req.Destination = c.ObjectAttrs.toRawObject(c.dst.bucket)
- for _, src := range c.srcs {
- if err := src.validate(); err != nil {
- return nil, err
- }
- if src.bucket != c.dst.bucket {
- return nil, fmt.Errorf("storage: all source objects must be in bucket %q, found %q", c.dst.bucket, src.bucket)
- }
- if src.encryptionKey != nil {
- return nil, fmt.Errorf("storage: compose source %s.%s must not have encryption key", src.bucket, src.object)
- }
- srcObj := &raw.ComposeRequestSourceObjects{
- Name: src.object,
- }
- if err := applyConds("ComposeFrom source", src.gen, src.conds, composeSourceObj{srcObj}); err != nil {
- return nil, err
- }
- req.SourceObjects = append(req.SourceObjects, srcObj)
- }
- call := c.dst.c.raw.Objects.Compose(c.dst.bucket, c.dst.object, req).Context(ctx)
- if err := applyConds("ComposeFrom destination", c.dst.gen, c.dst.conds, call); err != nil {
- return nil, err
- }
- if c.dst.userProject != "" {
- call.UserProject(c.dst.userProject)
- }
- if err := setEncryptionHeaders(call.Header(), c.dst.encryptionKey, false); err != nil {
- return nil, err
- }
- var obj *raw.Object
- setClientHeader(call.Header())
- err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err })
- if err != nil {
- return nil, err
- }
- return newObject(obj), nil
- }
|