Cloud Storage (GCS) - Package cloud.google.com/go/storage (v1.21.0)

Package storage provides an easy way to work with Google Cloud Storage. Google Cloud Storage stores data in named objects, which are grouped into buckets.

More information about Google Cloud Storage is available at https://cloud.google.com/storage/docs.

See https://pkg.go.dev/cloud.google.com/go for authentication, timeouts, connection pooling and similar aspects of this package.

Creating a Client

To start working with this package, create a client:

ctx := context.Background()
client, err := storage.NewClient(ctx)
if err != nil {
    // TODO: Handle error.
}

The client will use your default application credentials. Clients should be reused instead of created as needed. The methods of Client are safe for concurrent use by multiple goroutines.

If you only wish to access public data, you can create an unauthenticated client with

client, err := storage.NewClient(ctx, option.WithoutAuthentication())

To use an emulator with this library, you can set the STORAGE_EMULATOR_HOST environment variable to the address at which your emulator is running. This will send requests to that address instead of to Cloud Storage. You can then create and use a client as usual:

// Set STORAGE_EMULATOR_HOST environment variable.
err := os.Setenv("STORAGE_EMULATOR_HOST", "localhost:9000")
if err != nil {
    // TODO: Handle error.
}

// Create client as usual.
client, err := storage.NewClient(ctx)
if err != nil {
    // TODO: Handle error.
}

// This request is now directed to http://localhost:9000/storage/v1/b
// instead of https://storage.googleapis.com/storage/v1/b
if err := client.Bucket("my-bucket").Create(ctx, projectID, nil); err != nil {
    // TODO: Handle error.
}

Please note that there is no official emulator for Cloud Storage.

Buckets

A Google Cloud Storage bucket is a collection of objects. To work with a bucket, make a bucket handle:

bkt := client.Bucket(bucketName)

A handle is a reference to a bucket. You can have a handle even if the bucket doesn't exist yet. To create a bucket in Google Cloud Storage, call Create on the handle:

if err := bkt.Create(ctx, projectID, nil); err != nil {
    // TODO: Handle error.
}

Note that although buckets are associated with projects, bucket names are global across all projects.

Each bucket has associated metadata, represented in this package by BucketAttrs. The third argument to BucketHandle.Create allows you to set the initial BucketAttrs of a bucket. To retrieve a bucket's attributes, use Attrs:

attrs, err := bkt.Attrs(ctx)
if err != nil {
    // TODO: Handle error.
}
fmt.Printf("bucket %s, created at %s, is located in %s with storage class %s\n",
    attrs.Name, attrs.Created, attrs.Location, attrs.StorageClass)

Objects

An object holds arbitrary data as a sequence of bytes, like a file. You refer to objects using a handle, just as with buckets, but unlike buckets you don't explicitly create an object. Instead, the first time you write to an object it will be created. You can use the standard Go io.Reader and io.Writer interfaces to read and write object data:

obj := bkt.Object("data")
// Write something to obj.
// w implements io.Writer.
w := obj.NewWriter(ctx)
// Write some text to obj. This will either create the object or overwrite whatever is there already.
if _, err := fmt.Fprintf(w, "This object contains text.\n"); err != nil {
    // TODO: Handle error.
}
// Close, just like writing a file.
if err := w.Close(); err != nil {
    // TODO: Handle error.
}

// Read it back.
r, err := obj.NewReader(ctx)
if err != nil {
    // TODO: Handle error.
}
defer r.Close()
if _, err := io.Copy(os.Stdout, r); err != nil {
    // TODO: Handle error.
}
// Prints "This object contains text."

Objects also have attributes, which you can fetch with Attrs:

objAttrs, err := obj.Attrs(ctx)
if err != nil {
    // TODO: Handle error.
}
fmt.Printf("object %s has size %d and can be read using %s\n",
    objAttrs.Name, objAttrs.Size, objAttrs.MediaLink)

Listing objects

Listing objects in a bucket is done with the Bucket.Objects method:

query := &storage.Query{Prefix: ""}

var names []string
it := bkt.Objects(ctx, query)
for {
    attrs, err := it.Next()
    if err == iterator.Done {
        break
    }
    if err != nil {
        log.Fatal(err)
    }
    names = append(names, attrs.Name)
}

Objects are listed lexicographically by name. To filter objects lexicographically, Query.StartOffset and/or Query.EndOffset can be used:

query := &storage.Query{
    Prefix: "",
    StartOffset: "bar/",  // Only list objects lexicographically >= "bar/"
    EndOffset: "foo/",    // Only list objects lexicographically < "foo/"
}

// ... as before

If only a subset of object attributes is needed when listing, specifying this subset using Query.SetAttrSelection may speed up the listing process:

query := &storage.Query{Prefix: ""}
query.SetAttrSelection([]string{"Name"})

// ... as before

ACLs

Both objects and buckets have ACLs (Access Control Lists). An ACL is a list of ACLRules, each of which specifies the role of a user, group or project. ACLs are suitable for fine-grained control, but you may prefer using IAM to control access at the project level (see https://cloud.google.com/storage/docs/access-control/iam).

To list the ACLs of a bucket or object, obtain an ACLHandle and call its List method:

acls, err := obj.ACL().List(ctx)
if err != nil {
    // TODO: Handle error.
}
for _, rule := range acls {
    fmt.Printf("%s has role %s\n", rule.Entity, rule.Role)
}

You can also set and delete ACLs.

Conditions

Every object has a generation and a metageneration. The generation changes whenever the content changes, and the metageneration changes whenever the metadata changes. Conditions let you check these values before an operation; the operation only executes if the conditions match. You can use conditions to prevent race conditions in read-modify-write operations.

For example, say you've read an object's metadata into objAttrs. Now you want to write to that object, but only if its contents haven't changed since you read it. Here is how to express that:

w = obj.If(storage.Conditions{GenerationMatch: objAttrs.Generation}).NewWriter(ctx)
// Proceed with writing as above.

Signed URLs

You can obtain a URL that lets anyone read or write an object for a limited time. You don't need to create a client to do this. See the documentation of SignedURL for details.

url, err := storage.SignedURL(bucketName, "shared-object", opts)
if err != nil {
    // TODO: Handle error.
}
fmt.Println(url)

Post Policy V4 Signed Request

A type of signed request that allows uploads through HTML forms directly to Cloud Storage with temporary permission. Conditions can be applied to restrict how the HTML form is used and exercised by a user.

For more information, please see https://cloud.google.com/storage/docs/xml-api/post-object as well as the documentation of GenerateSignedPostPolicyV4.

pv4, err := storage.GenerateSignedPostPolicyV4(bucketName, objectName, opts)
if err != nil {
    // TODO: Handle error.
}
fmt.Printf("URL: %s\nFields; %v\n", pv4.URL, pv4.Fields)

Errors

Errors returned by this client are often of the type googleapi.Error. These errors can be introspected for more information by using errors.As with the richer googleapi.Error type. For example:

var e *googleapi.Error
if ok := errors.As(err, &e); ok {
      if e.Code == 409 { ... }
}

See https://pkg.go.dev/google.golang.org/api/googleapi#Error for more information.

Retrying failed requests

Methods in this package may retry calls that fail with transient errors. Retrying continues indefinitely unless the controlling context is canceled, the client is closed, or a non-transient error is received. To stop retries from continuing, use context timeouts or cancellation.

The retry strategy in this library follows best practices for Cloud Storage. By default, operations are retried only if they are idempotent, and exponential backoff with jitter is employed. In addition, errors are only retried if they are defined as transient by the service. See https://cloud.google.com/storage/docs/retry-strategy for more information.

Users can configure non-default retry behavior for a single library call (using BucketHandle.Retryer and ObjectHandle.Retryer) or for all calls made by a client (using Client.SetRetry). For example:

o := client.Bucket(bucket).Object(object).Retryer(
    // Use WithBackoff to change the timing of the exponential backoff.
    storage.WithBackoff(gax.Backoff{
        Initial:    2 * time.Second,
    }),
    // Use WithPolicy to configure the idempotency policy. RetryAlways will
    // retry the operation even if it is non-idempotent.
    storage.WithPolicy(storage.RetryAlways),
)

// Use a context timeout to set an overall deadline on the call, including all
// potential retries.
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()

// Delete an object using the specified strategy and timeout.
if err := o.Delete(ctx); err != nil {
    // Handle err.
}

Constants

DeleteAction, SetStorageClassAction

const (

	// DeleteAction is a lifecycle action that deletes a live and/or archived
	// objects. Takes precedence over SetStorageClass actions.
	DeleteAction = "Delete"

	// SetStorageClassAction changes the storage class of live and/or archived
	// objects.
	SetStorageClassAction = "SetStorageClass"
)

NoPayload, JSONPayload

const (
	// Send no payload with notification messages.
	NoPayload = "NONE"

	// Send object metadata as JSON with notification messages.
	JSONPayload = "JSON_API_V1"
)

Values for Notification.PayloadFormat.

ObjectFinalizeEvent, ObjectMetadataUpdateEvent, ObjectDeleteEvent, ObjectArchiveEvent

const (
	// Event that occurs when an object is successfully created.
	ObjectFinalizeEvent = "OBJECT_FINALIZE"

	// Event that occurs when the metadata of an existing object changes.
	ObjectMetadataUpdateEvent = "OBJECT_METADATA_UPDATE"

	// Event that occurs when an object is permanently deleted.
	ObjectDeleteEvent = "OBJECT_DELETE"

	// Event that occurs when the live version of an object becomes an
	// archived version.
	ObjectArchiveEvent = "OBJECT_ARCHIVE"
)

Values for Notification.EventTypes.

ScopeFullControl, ScopeReadOnly, ScopeReadWrite

const (
	// ScopeFullControl grants permissions to manage your
	// data and permissions in Google Cloud Storage.
	ScopeFullControl = raw.DevstorageFullControlScope

	// ScopeReadOnly grants permissions to
	// view your data in Google Cloud Storage.
	ScopeReadOnly = raw.DevstorageReadOnlyScope

	// ScopeReadWrite grants permissions to manage your
	// data in Google Cloud Storage.
	ScopeReadWrite = raw.DevstorageReadWriteScope
)

Variables

ErrBucketNotExist, ErrObjectNotExist

var (
	// ErrBucketNotExist indicates that the bucket does not exist.
	ErrBucketNotExist = errors.New("storage: bucket doesn't exist")
	// ErrObjectNotExist indicates that the object does not exist.
	ErrObjectNotExist = errors.New("storage: object doesn't exist")
)

Functions

func SignedURL

func SignedURL(bucket, object string, opts *SignedURLOptions) (string, error)

SignedURL returns a URL for the specified object. Signed URLs allow anyone access to a restricted resource for a limited time without needing a Google account or signing in. For more information about signed URLs, see https://cloud.google.com/storage/docs/accesscontrol#signed_urls_query_string_authentication

Example

package main

import (
	"fmt"
	"io/ioutil"
	"time"

	"cloud.google.com/go/storage"
)

func main() {
	pkey, err := ioutil.ReadFile("my-private-key.pem")
	if err != nil {
		// TODO: handle error.
	}
	url, err := storage.SignedURL("my-bucket", "my-object", &storage.SignedURLOptions{
		GoogleAccessID: "[email protected]",
		PrivateKey:     pkey,
		Method:         "GET",
		Expires:        time.Now().Add(48 * time.Hour),
	})
	if err != nil {
		// TODO: handle error.
	}
	fmt.Println(url)
}

ACLEntity

type ACLEntity string

ACLEntity refers to a user or group. They are sometimes referred to as grantees.

It could be in the form of: "user-

Or one of the predefined constants: AllUsers, AllAuthenticatedUsers.

AllUsers, AllAuthenticatedUsers

const (
	AllUsers              ACLEntity = "allUsers"
	AllAuthenticatedUsers ACLEntity = "allAuthenticatedUsers"
)

ACLHandle

type ACLHandle struct {
	// contains filtered or unexported fields
}

ACLHandle provides operations on an access control list for a Google Cloud Storage bucket or object.

func (*ACLHandle) Delete

func (a *ACLHandle) Delete(ctx context.Context, entity ACLEntity) (err error)

Delete permanently deletes the ACL entry for the given entity.

Example

package main

import (
	"context"

	"cloud.google.com/go/storage"
)

func main() {
	ctx := context.Background()
	client, err := storage.NewClient(ctx)
	if err != nil {
		// TODO: handle error.
	}
	// No longer grant access to the bucket to everyone on the Internet.
	if err := client.Bucket("my-bucket").ACL().Delete(ctx, storage.AllUsers); err != nil {
		// TODO: handle error.
	}
}

func (*ACLHandle) List

func (a *ACLHandle) List(ctx context.Context) (rules []ACLRule, err error)

List retrieves ACL entries.

Example

package main

import (
	"context"
	"fmt"

	"cloud.google.com/go/storage"
)

func main() {
	ctx := context.Background()
	client, err := storage.NewClient(ctx)
	if err != nil {
		// TODO: handle error.
	}
	// List the default object ACLs for my-bucket.
	aclRules, err := client.Bucket("my-bucket").DefaultObjectACL().List(ctx)
	if err != nil {
		// TODO: handle error.
	}
	fmt.Println(aclRules)
}

func (*ACLHandle) Set

func (a *ACLHandle) Set(ctx context.Context, entity ACLEntity, role ACLRole) (err error)

Set sets the role for the given entity.

Example

package main

import (
	"context"

	"cloud.google.com/go/storage"
)

func main() {
	ctx := context.Background()
	client, err := storage.NewClient(ctx)
	if err != nil {
		// TODO: handle error.
	}
	// Let any authenticated user read my-bucket/my-object.
	obj := client.Bucket("my-bucket").Object("my-object")
	if err := obj.ACL().Set(ctx, storage.AllAuthenticatedUsers, storage.RoleReader); err != nil {
		// TODO: handle error.
	}
}

ACLRole

type ACLRole string

ACLRole is the level of access to grant.

RoleOwner, RoleReader, RoleWriter

const (
	RoleOwner  ACLRole = "OWNER"
	RoleReader ACLRole = "READER"
	RoleWriter ACLRole = "WRITER"
)

ACLRule

type ACLRule struct {
	Entity      ACLEntity
	EntityID    string
	Role        ACLRole
	Domain      string
	Email       string
	ProjectTeam *