func (c *cloudStorageFS) Walk(ctx context.Context, path string, fn WalkFn) error {
	bh, err := c.bucketHandle(ctx, ScopeRead)
	if err != nil {
		return err
	}
	it := bh.List(&blob.ListOptions{
		Prefix: path,
	})
	for {
		r, err := it.Next(ctx)
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}
		if err = fn(r.Key); err != nil {
			return err
		}
	}
	return nil
}
Walk implements FS.
package storage
import (
	"context"
	"fmt"
	"io"
	"sync"
	"github.com/pkg/errors"
	"gocloud.dev/blob"
	"gocloud.dev/blob/gcsblob"
	"gocloud.dev/gcerrors"
	"gocloud.dev/gcp"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/storage/v1"
)
var ErrCredentialsMissing = errors.New("credentials missing")
func NewCloudStorageFS(bucket string, credentials *google.Credentials) FS {
	return &cloudStorageFS{
		bucketName:  bucket,
		credentials: credentials,
	}
}
type cloudStorageFS struct {
	l            sync.RWMutex
	bucket       *blob.Bucket
	bucketName   string
	bucketScopes Scope
	credentials  *google.Credentials
}
func (c *cloudStorageFS) URL(ctx context.Context, path string, options *SignedURLOptions) (string, error) {
	b, err := c.bucketHandle(ctx, ScopeSignURL)
	if err != nil {
		return "", err
	}
	var blobOptions *blob.SignedURLOptions
	if options != nil {
		o := blob.SignedURLOptions(*options)
		blobOptions = &o
	}
	return b.SignedURL(ctx, path, blobOptions)
}
func (c *cloudStorageFS) Open(ctx context.Context, path string, options *ReaderOptions) (*File, error) {
	b, err := c.bucketHandle(ctx, ScopeRead)
	if err != nil {
		return nil, err
	}
	f, err := b.NewReader(ctx, path, nil)
	if err != nil {
		if gcerrors.Code(err) == gcerrors.NotFound {
			return nil, &notExistError{
				Path: path,
			}
		}
		return nil, err
	}
	return &File{
		ReadCloser: f,
		Attributes: Attributes{
			ContentType: f.ContentType(),
			Size:        f.Size(),
			ModTime:     f.ModTime(),
		},
	}, nil
}
func (c *cloudStorageFS) Attributes(ctx context.Context, path string, options *ReaderOptions) (*Attributes, error) {
	b, err := c.bucketHandle(ctx, ScopeRead)
	if err != nil {
		return nil, err
	}
	a, err := b.Attributes(ctx, path)
	if err != nil {
		return nil, err
	}
	return &Attributes{
		ContentType: a.ContentType,
		Metadata:    a.Metadata,
		ModTime:     a.ModTime,
		Size:        a.Size,
	}, nil
}
func (c *cloudStorageFS) Create(ctx context.Context, path string, options *WriterOptions) (io.WriteCloser, error) {
	b, err := c.bucketHandle(ctx, ScopeWrite)
	if err != nil {
		return nil, err
	}
	var blobOpts *blob.WriterOptions
	if options != nil {
		blobOpts = &blob.WriterOptions{
			Metadata:    options.Attributes.Metadata,
			ContentType: options.Attributes.ContentType,
			BufferSize:  options.BufferSize,
		}
	}
	return b.NewWriter(ctx, path, blobOpts)
}
func (c *cloudStorageFS) Delete(ctx context.Context, path string) error {
	b, err := c.bucketHandle(ctx, ScopeDelete)
	if err != nil {
		return err
	}
	return b.Delete(ctx, path)
}