DigitalOcean Spaces from Delphi

Point the AWS SDK for Delphi at DigitalOcean Spaces: the endpoint and region split, per-bucket access keys, the built-in CDN, and a worked example.

DigitalOcean Spaces from Delphi
Contents
  1. Setting up on Spaces
  2. Access keys
  3. Configuring the SDK for Spaces
  4. A worked example
  5. The CDN
  6. Where Spaces differs
  7. What's next

DigitalOcean Spaces is object storage sold the way the rest of DigitalOcean is sold: a flat monthly base that bundles a storage allowance and an outbound transfer allowance, with per-unit charges once you pass either. It speaks the S3 protocol, so the AWS SDK for Delphi reaches it with the endpoint mechanism from Part 1.

Spaces also includes a CDN. A bucket can have one put in front of it from the control panel, with a generated edge hostname or a domain of your own, at no extra charge. For an application that serves files to browsers, that's a piece of infrastructure you'd otherwise have to assemble.

This post covers creating a bucket, the access keys, the endpoint and region settings, a worked example, and how the CDN fits alongside the SDK.

Setting up on Spaces

From the DigitalOcean control panel, open Spaces Object Storage and create a bucket. You'll choose a datacenter region and a name.

Regions span North America, Europe and Asia-Pacific, identified by the same slugs DigitalOcean uses for Droplets: nyc3, ams3, fra1, lon1, sgp1, syd1 and so on. Pick the one nearest whatever is doing the reading. An account can hold buckets in any of them.

You'll also pick a storage class. Standard is the general-purpose one. Cold Storage costs less per gigabyte stored and charges for retrieval. It also bills each object for at least 30 days and at least 128 KiB, so it suits larger files that are written once and rarely read. And it can't sit behind the CDN, which comes up again below.

Create a Spaces Bucket

Names are unique across all of Spaces rather than within your account, so expect the obvious ones to be taken. The form shows the bucket's origin URL as you type.

Bucket name and origin URL

Access keys

Spaces credentials are not DigitalOcean API tokens. They're a separate key pair, created under the Access Keys tab on the Spaces Object Storage page.

A key can have Full Access to every bucket in the account, or Limited Access to the buckets you select, with either Read or Read/Write/Delete permission on each. Limited keys are the better habit: an application that only ever writes to one bucket has no reason to hold a credential that reaches the others.

The secret is shown once, at creation. Copy it before you navigate away.

Configuring the SDK for Spaces

Endpoint and region go in the config file, the key pair in the credentials file:

# ~/.aws/config
[profile spaces]
region = us-east-1
s3 =
  endpoint_url = https://lon1.digitaloceanspaces.com
# ~/.aws/credentials
[spaces]
aws_access_key_id = <spaces-access-key>
aws_secret_access_key = <spaces-secret>

The endpoint hostname carries the geography: a bucket in lon1 is reached at https://lon1.digitaloceanspaces.com. The region setting is the literal string us-east-1 whatever datacenter the bucket is in, because that's the value DigitalOcean's documentation asks S3 SDKs to use. It doesn't route anything through the United States or cost you performance; the endpoint decides where the request goes.

Nothing else about credential handling changes. The Spaces key pair drops into the slots the provider chain already reads.

A worked example

Point the client at the profile, put a file, get it back:

uses
  AWS.S3;

var
  Options: IS3Options;
  Client: IS3Client;
  Obj: IS3Object;
begin
  Options := TS3Options.Create;
  Options.Profile := 'spaces';
  Client := TS3Client.Create(Options);

  Obj := TS3Object.Create('appercept-reports', 'quarterly.pdf', Client);
  Obj.UploadFile('C:\Reports\quarterly.pdf');
  Obj.DownloadFile('C:\Downloads\quarterly.pdf');
end;

That is the B2 example with a different profile name. Run it and refresh the bucket in the control panel; the object should be there.

The CDN

Tick Enable CDN when you create the bucket, or turn it on later from its Settings tab, and Spaces gives you an edge hostname alongside the origin one, in the form <bucket>.<region>.cdn.digitaloceanspaces.com. The same object is then reachable two ways: through the origin endpoint the SDK writes to, and through the cached edge hostname. Bandwidth served through the CDN counts toward the same transfer allowance as everything else.

Your Delphi code keeps talking to the origin endpoint, because that's where writes go and the SDK is already configured for it. What you hand to a browser, put in an email, or store as a URL in your database is the edge hostname. Nothing in the upload path changes.

A custom subdomain works too (files.example.com pointed at the bucket), and DigitalOcean requires a certificate for it, either one it provisions through Let's Encrypt or one you upload.

Requests made with presigned URLs aren't cached by the CDN. That suits a link signed for one request, but it means the CDN does nothing for that access pattern. And because Cold Storage buckets can't use the CDN, the cheaper storage class and the edge cache are an either/or choice for each bucket.

Where Spaces differs

The core object operations behave as they do anywhere else, including both ListObjects and ListObjectsV2. Past those:

  • Versioning. Supported through the API, but not exposed in the control panel. If you want it, your code or your tooling has to turn it on.
  • Lifecycle rules. Time-based expiration and cleanup of incomplete multipart uploads work. Tag-based rules don't.
  • ACLs. The two canned values, private and public-read.
  • Bucket policies. Supported through the API only, as with versioning.
  • Encryption. Customer-provided keys (SSE-C). Bucket-level default encryption settings aren't supported.
  • Presigned URLs. Supported, with SigV4 (which the SDK uses) and the older SigV2. The presigned URLs post applies unchanged.
  • Multipart uploads. Supported, though copies that cross regions aren't.

Object Lock, replication and object tagging don't appear in DigitalOcean's compatibility list. Spaces answers any operation it doesn't implement with a standard NotImplemented error, so an unsupported call shows itself the first time you make it.

What's next

Next in the series: Hetzner Object Storage from Delphi. A European provider with its own endpoint and region conventions, and the EU data-residency angle.