Google.Cloud.Storage.V1

Google.Cloud.Storage.V1 is a.NET client library for theGoogle Cloud Storage API.It wraps theGoogle.Apis.Storage.v1 generated library, providing a higher-level API to make it easier to use.

Note:This documentation is for version4.13.0 of the library.Some samples may not work with other versions.

Installation

Install theGoogle.Cloud.Storage.V1 package from NuGet. Add it toyour project in the normal way (for example by right-clicking on theproject in Visual Studio and choosing "Manage NuGet Packages...").

Authentication

When running on Google Cloud, no action needs to be taken to authenticate.

Otherwise, the simplest way of authenticating your API calls is toset up Application Default Credentials.The credentials will automatically be used to authenticate. SeeSet up Application Default Credentials for more details.

Getting started

Common operations are exposed via theStorageClient class.

Error responses

All errors reported by the underlying API (including preconditionfailures) are propagated as exceptions of typeGoogleApiException.See theCloud Storage API status and error codesdocumentationfor details of the HTTP status codes used.

Client life-cycle management

In many cases you don't need to worry about disposing ofStorageClient objects, and can create them reasonably freely -but be aware that thiscan causes issues with memory and networkconnection usage. We advise you to reuse a single client object if possible.StorageClient is thread-safe, so in most cases a single object isthe simplest option.

If your architecture requires you to frequently create new clientobjects, please dispose of them to help with timely resourceclean-up. Seethe resource clean-upguidefor more details.

Sample code

var client = StorageClient.Create();// Create a bucket with a globally unique namevar bucketName = Guid.NewGuid().ToString();var bucket = client.CreateBucket(projectId, bucketName);// Upload some filesvar content = Encoding.UTF8.GetBytes("hello, world");var obj1 = client.UploadObject(bucketName, "file1.txt", "text/plain", new MemoryStream(content));var obj2 = client.UploadObject(bucketName, "folder1/file2.txt", "text/plain", new MemoryStream(content));// List objectsforeach (var obj in client.ListObjects(bucketName, "")){    Console.WriteLine(obj.Name);}// Download fileusing (var stream = File.OpenWrite("file1.txt")){    client.DownloadObject(bucketName, "file1.txt", stream);}

Signed URLs

Signed URLs can be created to provide limited access to specific buckets andobjects to anyone in possession of the URL, regardless of whether they havea Google account.

For example, Signed URLs can be created to provide read-only access toexisting objects:

// Create a signed URL which can be used to get a specific object for one hour.UrlSigner urlSigner = UrlSigner.FromCredential(credential);string url = await urlSigner.SignAsync(bucketName, objectName, TimeSpan.FromHours(1));// Get the content at the created URL.HttpResponseMessage response = await httpClient.GetAsync(url);string content = await response.Content.ReadAsStringAsync();

Or write-only access to put specific object content into a bucket:

// Create a request template that will be used to create the signed URL.var destination = "places/world.txt";UrlSigner.RequestTemplate requestTemplate = UrlSigner.RequestTemplate    .FromBucket(bucketName)    .WithObjectName(destination)    .WithHttpMethod(HttpMethod.Put)    .WithContentHeaders(new Dictionary<string, IEnumerable<string>>    {        { "Content-Type", new[] { "text/plain" } }    });// Create options specifying for how long the signer URL will be valid.UrlSigner.Options options = UrlSigner.Options.FromDuration(TimeSpan.FromHours(1));// Create a signed URL which allows the requester to PUT data with the text/plain content-type.UrlSigner urlSigner = UrlSigner.FromCredential(credential);string url = await urlSigner.SignAsync(requestTemplate, options);// Upload the content into the bucket using the signed URL.string source = "world.txt";ByteArrayContent content;using (FileStream stream = File.OpenRead(source)){    byte[] data = new byte[stream.Length];    stream.Read(data, 0, data.Length);    content = new ByteArrayContent(data)    {        Headers = { ContentType = new MediaTypeHeaderValue("text/plain") }    };}HttpResponseMessage response = await httpClient.PutAsync(url, content);

Supported credential types for URL signing

Google.Apis.Auth.OAuth2.ServiceAccountCredential,Google.Apis.Auth.OAuth2.ComputeCredentialandGoogle.Apis.Auth.OAuth2.ImpersonatedCredential are all supported credentials fromwhich you can build aUrlSigner by calling the appropiateUrlSigner.FromCredentialmethod overload.Google.Apis.Auth.OAuth2.GoogleCredential is also supported as long as theunderlying credential is one of the supported specific types. The following example demonstrateshow to explicitly use a Compute credential for URL signing.

// Create a signed URL which can be used to get a specific object for one hour.var computeCredential = new ComputeCredential();UrlSigner urlSigner = UrlSigner.FromCredential(computeCredential);string url = await urlSigner.SignAsync(bucketName, objectName, TimeSpan.FromHours(1));// Get the content at the created URL.HttpResponseMessage response = await httpClient.GetAsync(url);string content = await response.Content.ReadAsStringAsync();

When using aUrlSigner it's worth being aware of the underlying synchronous/asynchronous natureof the signing operations depending on the credential type the signer was created from. If you useda service account credential, signing happens locally and the signing operation is synchronous.But if you use an impersonated credential or a Compute credential, then a request to the IAM APIis made for signing and the operation is asynchronous. In this case, you can still use thesynchronous versions of the signing methods but they will block until the asynchronous operationhas completed, which could lead to deadlocks. In general, if you are unsure of which credentialwas used to create a given URL signer, it is safer to use the asynchronous signing methods.

HMAC Signed URLs

If you have access to an HMAC key, you can also sign URLs, even if youdon't have access to a service account private key. See theHMAC Keys documentationand theSigning documentationfor more details. Below you can find an example on how to createHMAC signed URLs using this library:

// Create an HmacBlobSigner from an HMAC Key ID and Secret.UrlSigner.IBlobSigner blobSigner = UrlSigner.HmacBlobSigner.Create(hmacKeyId, hmacKeySecret);// Create a URL signer from the HmacBlobSigner.UrlSigner urlSigner = UrlSigner.FromBlobSigner(blobSigner);// Create an HMAC signed URL which can be used to get a specific object for one hour.string url = urlSigner.Sign(bucketName, objectName, TimeSpan.FromHours(1));// Get the content at the created URL.HttpResponseMessage response = await httpClient.GetAsync(url);string content = await response.Content.ReadAsStringAsync();

Signing URLs with a custom blob signer

If you need to sign URLs but none of the supported signer optionsapply to your use case, you can create aUrlSigner.IBlobSignerimplementation to perform the signing part. Use theUrlSigner.FromBlobSigner(UrlSigner.IBlobSigner) method to obtaina URL signer that uses your custom signer implementation.

Specifying the signing version

(V4 signing is currently beta functionality.)

Google Cloud Storage supports two signing process versions: V2 and V4.Currently the default is V2, although in the future the library maybe updated to use V4 by default.

To specify the URL signing versioning, use theUrlSigner.Options.WithSigningVersionmethod, specifying the signing version you wish to use. This doesnot change the UrlSigner it is called on; it returns a new UrlSignerthat uses the specified version.

Note that V4 signing is restricted to generating URLs that are validfor at most 7 days.

// Create a signed URL which can be used to get a specific object for one hour,// using the V4 signing process.UrlSigner urlSigner = UrlSigner.FromCredential(credential);string url = await urlSigner.SignAsync(bucketName, objectName, TimeSpan.FromHours(1), signingVersion: SigningVersion.V4);// Get the content at the created URL.HttpResponseMessage response = await httpClient.GetAsync(url);string content = await response.Content.ReadAsStringAsync();

Uploading objects by using HTML forms

In some cases, you might need to allow your users to upload objects via HTML forms.You can create signed POST policies that specify what is and is not allowed in suchscenarios.You can read thePolicy Documentdocumentation to get more information on how a POST policy document should be built.You can read thePOST objectdocumentation to get more details on how the forms should be built.

Below you will find some samples on how to create a signed POST policy.

Simplest approach, where you restrict the upload to a specific bucket and a specific object name.

// Create a signed post policy which can be used to upload a specific object and// expires in 1 hour after creation.UrlSigner urlSigner = UrlSigner.FromCredential(credential);UrlSigner.Options options = UrlSigner.Options    .FromDuration(TimeSpan.FromHours(1))    .WithSigningVersion(SigningVersion.V4)    .WithScheme("https");UrlSigner.PostPolicy postPolicy = UrlSigner.PostPolicy.ForBucketAndKey(bucketName, objectName);postPolicy.SetCustomField(UrlSigner.PostPolicyCustomElement.GoogleMetadata, "x-goog-meta-test", "data");UrlSigner.SignedPostPolicy signedPostPolicy = await urlSigner.SignAsync(postPolicy, options);// Create an HTML form including all the fields in the signed post policy.StringBuilder form = new StringBuilder();form.AppendLine($"<form action=\"{signedPostPolicy.PostUrl}\" method=\"post\" enctype=\"multipart/form-data\">");foreach (var field in signedPostPolicy.Fields){    form.AppendLine($"<input type=\"hidden\" name=\"{field.Key}\" value=\"{field.Value}\">");}// Include the file element. It should always be the last element in the form.form.AppendLine("<input name=\"file\" type=\"file\">");form.AppendLine("<input type=\"submit\" value=\"Upload\">");form.AppendLine("</form>");// You can now save the form to file and serve it as static content// or send it as the response to a request made to your application.File.WriteAllText("PostPolicySimple.html", form.ToString());

Enforce how browser's cache will treat the uploaded object.

// Create a signed post policy which can be used to upload a specific object with a// specific cache-control value and expires in 1 hour after creation.UrlSigner urlSigner = UrlSigner.FromCredential(credential);UrlSigner.Options options = UrlSigner.Options    .FromDuration(TimeSpan.FromHours(1))    .WithSigningVersion(SigningVersion.V4)    .WithScheme("https");UrlSigner.PostPolicy postPolicy = UrlSigner.PostPolicy.ForBucketAndKey(bucketName, objectName);postPolicy.SetField(UrlSigner.PostPolicyStandardElement.Acl, "public-read");postPolicy.SetField(UrlSigner.PostPolicyStandardElement.CacheControl, "public,max-age=86400");UrlSigner.SignedPostPolicy signedPostPolicy = await urlSigner.SignAsync(postPolicy, options);// Create an HTML form including all the fields in the signed post policy.StringBuilder form = new StringBuilder();form.AppendLine($"<form action=\"{signedPostPolicy.PostUrl}\" method=\"post\" enctype=\"multipart/form-data\">");foreach (var field in signedPostPolicy.Fields){    form.AppendLine($"<input type=\"hidden\" name=\"{field.Key}\" value=\"{field.Value}\">");}// Include the file element. It should always be the last element in the form.form.AppendLine("<input name=\"file\" type=\"file\">");form.AppendLine("<input type=\"submit\" value=\"Upload\">");form.AppendLine("</form>");// You can now save the form to file and serve it as static content// or send it as the response to a request made to your application.File.WriteAllText("PostPolicyCacheControl.html", form.ToString());

You can also set starts-with conditions for some form elements. This means that theposting form should contain that element with a value that matches the condition.

// Create a signed post policy which can be used to upload a specific object and// expires in 10 seconds after creation.// It also sets a starts-with condition on the acl form element, that should be met// by the actual form used for posting.UrlSigner urlSigner = UrlSigner.FromCredential(credential);UrlSigner.Options options = UrlSigner.Options    .FromDuration(TimeSpan.FromHours(1))    .WithSigningVersion(SigningVersion.V4)    .WithScheme("https");UrlSigner.PostPolicy postPolicy = UrlSigner.PostPolicy.ForBucketAndKey(bucketName, objectName);postPolicy.SetStartsWith(UrlSigner.PostPolicyStandardElement.Acl, "public");UrlSigner.SignedPostPolicy signedPostPolicy = await urlSigner.SignAsync(postPolicy, options);// Create an HTML form including all the fields in the signed post policy.StringBuilder form = new StringBuilder();form.AppendLine($"<form action=\"{signedPostPolicy.PostUrl}\" method=\"post\" enctype=\"multipart/form-data\">");foreach (var field in signedPostPolicy.Fields){    form.AppendLine($"<input type=\"hidden\" name=\"{field.Key}\" value=\"{field.Value}\">");}// Include also an acl element with a value that meets the condition set in the policy.form.AppendLine("<input type=\"hidden\" name=\"acl\" value=\"public-read\">");// Include the file element. It should always be the last element in the form.form.AppendLine("<input name=\"file\" type=\"file\">");form.AppendLine("<input type=\"submit\" value=\"Upload\">");form.AppendLine("</form>");// You can now save the form to file and serve it as static content// or send it as the response to a request made to your application.File.WriteAllText("PostPolicyAcl.html", form.ToString());

Tell the server which HTTP status code you want it to return on succes.

// Create a signed post policy which can be used to upload a specific object and// expires in 1 hour after creation.// It also sets a specific HTTP success satus code that should be returned.// Only 200, 201 and 204 are allowed.UrlSigner urlSigner = UrlSigner.FromCredential(credential);UrlSigner.Options options = UrlSigner.Options    .FromDuration(TimeSpan.FromHours(1))    .WithSigningVersion(SigningVersion.V4)    .WithScheme("https");UrlSigner.PostPolicy postPolicy = UrlSigner.PostPolicy.ForBucketAndKey(bucketName, objectName);postPolicy.SetField(UrlSigner.PostPolicyStandardElement.SuccessActionStatus, HttpStatusCode.OK);UrlSigner.SignedPostPolicy signedPostPolicy = await urlSigner.SignAsync(postPolicy, options);// Create an HTML form including all the fields in the signed post policy.StringBuilder form = new StringBuilder();form.AppendLine($"<form action=\"{signedPostPolicy.PostUrl}\" method=\"post\" enctype=\"multipart/form-data\">");foreach (var field in signedPostPolicy.Fields){    form.AppendLine($"<input type=\"hidden\" name=\"{field.Key}\" value=\"{field.Value}\">");}// Include the file element. It should always be the last element in the form.form.AppendLine("<input name=\"file\" type=\"file\">");form.AppendLine("<input type=\"submit\" value=\"Upload\">");form.AppendLine("</form>");// You can now save the form to file and serve it as static content// or send it as the response to a request made to your application.File.WriteAllText("PostPolicySuccessStatus.html", form.ToString());

Upload URIs

In some cases, it may not make sense for client applications to have permissionsto begin an upload for an object, but an authenticated service may choose to grantthis ability for individual uploads. Signed URLs are one option for this. Anotheroption is for the service to start a resumable upload session, but instead ofperforming the upload, sending the resulting upload URI to the client applicationso it can perform the upload instead. Unlike sessions initiated with a signed URL,a pre-initated upload session will force the client application to upload throughthe region in which the session began, which will likely be close to the service,and not necessarily the client.

var client = StorageClient.Create();var source = "world.txt";var destination = "places/world.txt";var contentType = "text/plain";// var acl = PredefinedAcl.PublicRead // publicvar acl = PredefinedObjectAcl.AuthenticatedRead; // privatevar options = new UploadObjectOptions { PredefinedAcl = acl };var uploadUri = await client.InitiateUploadSessionAsync(bucketName, destination, contentType, contentLength: null, options);// Send uploadUri to (unauthenticated) client application, so it can perform the upload:using (var stream = File.OpenRead(source)){    // IUploadProgress defined in Google.Apis.Upload namespace    IProgress<IUploadProgress> progress = new Progress<IUploadProgress>(      p => Console.WriteLine($"bytes: {p.BytesSent}, status: {p.Status}")    );    var actualUploader = ResumableUpload.CreateFromUploadUri(uploadUri, stream);    actualUploader.ProgressChanged += progress.Report;    await actualUploader.UploadAsync();}

Customer-supplied encryption keys

Storage objects are always stored encrypted, but if you wish tospecify your own encryption key instead of using the server-suppliedone, you can do so either for all operations with a particularStorageClient or on individual ones.

// Use EncryptionKey.Create if you already have a key.EncryptionKey key = EncryptionKey.Generate();// This will affect all relevant object-based operations by default.var client = StorageClient.Create(encryptionKey: key);var content = Encoding.UTF8.GetBytes("hello, world");client.UploadObject(bucketName, "encrypted.txt", "text/plain", new MemoryStream(content));// When downloading, either use a client with the same key...client.DownloadObject(bucketName, "encrypted.txt", new MemoryStream());// Or specify a key just for that operation.var client2 = StorageClient.Create();client2.DownloadObject(bucketName, "encrypted.txt", new MemoryStream(),    new DownloadObjectOptions { EncryptionKey = key });

Change notification via Google Cloud Pub/Sub

You can configure a bucket to send a change notification to aGoogle Cloud Pub/Sub topicwhen changes occur. The sample below shows how to create a Pub/Subtopic, set its permissions so that the change notifications can bepublished to it, and then create the notification configuration on abucket. You'll need to add a dependency on theGoogle.Cloud.PubSub.V1 NuGet package to create the topic andmanage its permissions.

// First create a Pub/Sub topic.PublisherServiceApiClient publisherClient = PublisherServiceApiClient.Create();TopicName topicName = new TopicName(projectId, topicId);publisherClient.CreateTopic(topicName);// Prepare the topic for Storage notifications. The Storage Service Account must have Publish permission// for the topic. The code below adds the service account into the "roles/pubsub.publisher" role for the topic.// Determine the Storage Service Account name to use in IAM operations.StorageClient storageClient = StorageClient.Create();string storageServiceAccount = $"serviceAccount:{storageClient.GetStorageServiceAccountEmail(projectId)}";// Fetch the IAM policy for the topic.GetIamPolicyRequest getPolicyRequest = new GetIamPolicyRequest { ResourceAsResourceName = topicName };Iam.V1.Policy policy = publisherClient.IAMPolicyClient.GetIamPolicy(getPolicyRequest);var role = "roles/pubsub.publisher";// Ensure the Storage Service Account is in the publisher role, setting the IAM policy for the topic// on the server if necessary.if (policy.AddRoleMember(role, storageServiceAccount)){    SetIamPolicyRequest setPolicyRequest = new SetIamPolicyRequest    {        ResourceAsResourceName = topicName,        Policy = policy    };    publisherClient.IAMPolicyClient.SetIamPolicy(setPolicyRequest);}// Now that the topic is ready, we can create a notification configuration for StorageNotification notification = new Notification{    Topic = $"//pubsub.googleapis.com/{topicName}",    PayloadFormat = "JSON_API_V1"};notification = storageClient.CreateNotification(bucket, notification);Console.WriteLine($"Created notification ID: {notification.Id}");

Connecting to an emulator

StorageClient supports theFirebase StorageEmulator.

This is configured usingStorageClientBuilder.EmulatorDetection,as described in theclient library help page onemulators.

As well as setting theEmulatorDetection property when building aclient, you must set theSTORAGE_EMULATOR_HOST environmentvariable, in the formhost:port (orhttp://host:port), e.g.127.0.0.1:9199. The port is the one shown in the emulator UI; notethat this isnot the port of the Storage Emulator user interface.

Note that the Firebase Storage Emulator only supports a limited setof operations. Seethe Storage Emulatordocumentationfor more details.

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2026-01-26 UTC.