Cloud Storage (GCS)
floci-gcp emulates Google Cloud Storage using the real GCP wire protocols:
- REST XML — object operations (upload, download, delete, list objects)
- REST JSON — bucket management (create bucket, list buckets, get bucket metadata)
Configuration
| Variable | Default | Description |
|---|---|---|
FLOCI_GCP_SERVICES_GCS_ENABLED |
true |
Enable/disable Cloud Storage |
FLOCI_GCP_BASE_URL |
http://localhost:4588 |
Base URL embedded in object URLs and pre-signed URLs |
Emulator Variable
GCP Storage SDK clients use this variable to route requests to floci-gcp instead of storage.googleapis.com.
Quick Start
export STORAGE_EMULATOR_HOST=http://localhost:4588
# Create a bucket
gcloud storage buckets create gs://my-bucket
# Upload an object
echo "hello from floci-gcp" | gcloud storage cp - gs://my-bucket/hello.txt
# List objects
gcloud storage ls gs://my-bucket
# Download
gcloud storage cp gs://my-bucket/hello.txt -
# Delete
gcloud storage rm gs://my-bucket/hello.txt
Storage storage = StorageOptions.newBuilder()
.setHost("http://localhost:4588")
.setProjectId("floci-local")
.setCredentials(NoCredentials.getInstance())
.build()
.getService();
// Create bucket
Bucket bucket = storage.create(BucketInfo.of("my-bucket"));
// Upload object
Blob blob = storage.create(
BlobInfo.newBuilder("my-bucket", "hello.txt").build(),
"hello from floci-gcp".getBytes());
// Download object
byte[] content = storage.readAllBytes("my-bucket", "hello.txt");
// List objects
Page<Blob> blobs = storage.list("my-bucket");
blobs.iterateAll().forEach(b -> System.out.println(b.getName()));
// Delete object
storage.delete("my-bucket", "hello.txt");
import os
os.environ["STORAGE_EMULATOR_HOST"] = "http://localhost:4588"
from google.cloud import storage
client = storage.Client(project="floci-local")
# Create bucket
bucket = client.bucket("my-bucket")
client.create_bucket(bucket)
# Upload object
blob = bucket.blob("hello.txt")
blob.upload_from_string("hello from floci-gcp")
# Download object
content = blob.download_as_text()
# List objects
for b in client.list_blobs("my-bucket"):
print(b.name)
# Delete
blob.delete()
import { Storage } from "@google-cloud/storage";
const storage = new Storage({
apiEndpoint: "http://localhost:4588",
projectId: "floci-local",
});
// Create bucket
await storage.createBucket("my-bucket");
// Upload object
await storage.bucket("my-bucket").file("hello.txt")
.save("hello from floci-gcp");
// Download
const [content] = await storage.bucket("my-bucket").file("hello.txt").download();
// List objects
const [files] = await storage.bucket("my-bucket").getFiles();
files.forEach(f => console.log(f.name));
Multipart Upload
floci-gcp supports multipart (resumable) upload — the standard GCS mechanism for large objects. The GCP SDK uses this automatically for objects above a threshold.
Object Versioning
Enable versioning on a bucket:
Each overwrite creates a new object generation. List all versions:
Pre-signed URLs
Generate a pre-signed URL for temporary public access:
URL signedUrl = storage.signUrl(
BlobInfo.newBuilder("my-bucket", "hello.txt").build(),
15, TimeUnit.MINUTES,
Storage.SignUrlOption.withV4Signature());
Pre-signed URLs are generated using the FLOCI_GCP_BASE_URL as the base.
Virtual-Hosted Style URLs
floci-gcp supports virtual-hosted style GCS URLs:
The embedded DNS server resolves *.localhost.floci.io to floci-gcp's container IP when running inside Docker, so virtual-hosted URLs work from sidecar containers without extra DNS configuration.
Supported Operations
Bucket management (REST JSON):
CreateBucket(withlocation,storageClass,versioning,lifecycle,cors,retentionPolicy)GetBucketListBuckets(withpageTokenpagination)UpdateBucket/PatchBucketDeleteBucketGetBucketIamPolicy/SetBucketIamPolicy/TestBucketIamPermissions
Bucket ACLs (REST JSON):
ListBucketAcl/CreateBucketAclGetBucketAcl/UpdateBucketAcl/DeleteBucketAclListDefaultObjectAcl/CreateDefaultObjectAclGetDefaultObjectAcl/UpdateDefaultObjectAcl/DeleteDefaultObjectAcl
Object operations (REST XML + REST JSON):
PutObject(simple and multipart/resumable upload)GetObjectDeleteObjectListObjects(withpageToken,prefix,delimiterpagination)CopyObjectHeadObjectPatchObject(update metadata:contentType,contentDisposition,contentEncoding,contentLanguage, custom metadata)ComposeObject(concatenate up to 32 source objects)- Pre-signed GET/PUT URLs (V4 signature via IAM
SignBlob)
Object ACLs (REST JSON):
ListObjectAcl/CreateObjectAclGetObjectAcl/UpdateObjectAcl/DeleteObjectAcl
Conditional requests (preconditions):
ifGenerationMatch/ifGenerationNotMatchifMetagenerationMatch/ifMetagenerationNotMatch- Returns HTTP 412 on precondition failure