WriteableFileProviders.GoogleCloudStorage
2027.1.1
dotnet add package WriteableFileProviders.GoogleCloudStorage --version 2027.1.1
NuGet\Install-Package WriteableFileProviders.GoogleCloudStorage -Version 2027.1.1
<PackageReference Include="WriteableFileProviders.GoogleCloudStorage" Version="2027.1.1" />
<PackageVersion Include="WriteableFileProviders.GoogleCloudStorage" Version="2027.1.1" />
<PackageReference Include="WriteableFileProviders.GoogleCloudStorage" />
paket add WriteableFileProviders.GoogleCloudStorage --version 2027.1.1
#r "nuget: WriteableFileProviders.GoogleCloudStorage, 2027.1.1"
#:package WriteableFileProviders.GoogleCloudStorage@2027.1.1
#addin nuget:?package=WriteableFileProviders.GoogleCloudStorage&version=2027.1.1
#tool nuget:?package=WriteableFileProviders.GoogleCloudStorage&version=2027.1.1
WriteableFileProviders.GoogleCloudStorage
A Google Cloud Storage bucket as an IWriteableFileProvider, on Google.Cloud.Storage.V1 5. Part of
WriteableFileProviders.
Install
dotnet add package WriteableFileProviders.GoogleCloudStorage
Needs .NET 10. Not native AOT compatible: the Google Cloud Storage SDK reads and writes JSON through reflection. Keep every WriteableFileProviders package in an app on the same major version.
Use
using WriteableFileProviders;
using WriteableFileProviders.GoogleCloudStorage;
// No credentials in the options: Application Default Credentials (Workload Identity, the metadata server, gcloud).
await using var files = new WriteableGoogleCloudStorageFileProvider(new GoogleCloudStorageFileProviderOptions
{
BucketName = "my-bucket",
RootDirectoryPath = "tenants/acme",
});
await files.UpdateFileAsync("reports/2026.csv", stream, new UpdateFileOptions { ContentType = "text/csv" });
var url = await files.GetSignedUrlAsync("reports/2026.csv", new SignedUrlOptions { Validity = TimeSpan.FromMinutes(15) });
From configuration (services.AddWriteableFileProvider(section)):
{ "Files": { "Type": "WriteableGoogleCloudStorageFileProvider", "BucketName": "my-bucket", "ProjectId": "my-project" } }
Or pass a StorageClient you build (other credentials, retries, an encryption key). The provider never disposes it,
and reads only the bucket, the root, CreateBucketIfMissing and ProjectId from the options.
Configuration
| Option | Default | Meaning |
|---|---|---|
BucketName |
required | The bucket. |
ClientEmail, PrivateKey |
A service account's client_email and PEM private_key. Set both. Escaped \n in the key is fine. A secret. |
|
CredentialsJson |
A whole service account key file as one string. Other credential files throw ArgumentException. A secret. |
|
ProjectId |
The project. Only CreateBucketIfMissing needs it, and not when CredentialsJson (or the key file Application Default Credentials use) names it. |
|
Endpoint |
Google's | Another Cloud Storage endpoint, such as http://localhost:4443 for fake-gcs-server. Plain http only toward loopback. |
AllowInsecureHttp |
false |
Allow an http Endpoint toward a host that isn't loopback. Tokens then travel in clear text. |
CreateBucketIfMissing |
false |
Create a missing bucket on the first call, in the US multi-region with the Standard class. Without it, a missing bucket throws InvalidOperationException. |
RootDirectoryPath |
the bucket | The folder in the bucket the provider is confined to. |
Authentication
The first credential set wins: ClientEmail with PrivateKey, then CredentialsJson, then Application Default
Credentials. The constructor does no network I/O: Application Default Credentials are looked up on the first call,
which throws InvalidOperationException when there are none.
The credential needs storage.objects.list as well as object read and write (roles/storage.objectViewer and
roles/storage.objectAdmin have it): the provider lists to tell a folder from a missing file. Without it, what has to
list throws UnauthorizedAccessException: listings, GetFileInfoAsync of a path that isn't a file, deletes of a
folder or a missing path, copies, moves, appends to a new file, and seekable reads, local streams and read URLs of a
missing file. ReadFileAsync and ReadRangeAsync of a missing file still throw FileNotFoundException.
What's different
- ETags are generations.
IfMatchandIfNotExistsare generation preconditions, atomic. Appends rewrite the object on the generation read, so they never lose a concurrent write; after one they read the object again and append after it, up to five attempts in all. - Writes are resumable uploads. The object appears only when every byte has arrived.
- A
dir/object (the console's "create folder") is an empty folder. - Reads aren't checked against the object's hash.
- Moves copy on the server, then delete the exact generation copied; a source changed meanwhile undoes the move. A copy is never retried: after a server error or timeout it may have landed, so check the destination first.
- Signed URLs are V4 read, create and write URLs, from 1 second to 7 days. They need a service account key, or
iam.serviceAccounts.signBlobon the identity's service account; otherwise the gateway issues them. Uploads must sendSignedUrl.Headers. - Content type is the only metadata written.
WatchthrowsNotSupportedException.
Learn more
- The guide: reading and writing, signed URLs, errors, providers compared.
- Moving from 2026.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Google.Cloud.Storage.V1 (>= 5.0.0)
- WriteableFileProviders.Common (>= 2027.1.1 && < 2028.0.0-alpha)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2027.1.1 | 65 | 10/13/2026 |
| 2027.1.1-rc.1 | 33 | 10/13/2026 |
| 2027.1.0 | 79 | 10/12/2026 |
| 2027.1.0-rc.1 | 49 | 10/12/2026 |
| 2027.0.0 | 242 | 10/11/2026 |
| 2027.0.0-rc.2 | 49 | 10/11/2026 |
What each release changed: https://github.com/serionist/WriteableFileProviders/blob/main/CHANGELOG.md. Moving from 2026.x: https://github.com/serionist/WriteableFileProviders/blob/main/docs/migration-2027.md