This page covers reading objects from your server: get for the bytes, info for the facts, exists for a boolean, list for a page of keys, and signedReadUrl for a link that reads a private object without going through your server.
The records these return (BlobObject, BlobInfo, BlobDownload) are on Types.
get#
body is a stream and nothing is buffered for you. Wrap it in a Response for the usual conversions:
A missing object throws a BlobError with code not_found, status 404. See Errors.
There is no range option. For byte ranges, use an S3 client.
info#
One HEAD request. The same record as get without the bytes, so reading a 2 GB object's facts is cheap. A missing object throws not_found.
metadata comes back from get and info only, with keys lowercased. See Metadata.
exists#
The same HEAD request as info, answering false instead of throwing.
If you need the etag, size or metadata anyway, call info() and catch not_found instead of making two round trips.
list#
All three are optional.
| Option | Type | Default | Description |
|---|---|---|---|
prefix | string | none, the whole bucket | Only keys starting with this. |
limit | number | storage picks, at most 1000 | Page size, clamped to 1 to 1000. |
cursor | string | none, the first page | The cursor from the previous page. |
A full walk is a do ... while:
Each entry is a BlobObject: path, size, etag, timestamp and URLs. There is no contentType or metadata; reading those is one info() per object.
prefix is the only filter. There is no query by owner, type or date, so "this user's files" has to be a prefix you chose at upload time. An app that needs to query its files should keep its own table and treat the bucket as storage, not an index.
Public URLs#
Every record on a public bucket already carries url; publicUrl gives you one for any path. It returns undefined on a private bucket and throws a TypeError for an empty path or one with a . or .. segment.
The URL itself is built from the token, but whether the bucket has a public host at all is known only to the backend, so the first call on a fresh client fetches credentials. They are cached, so every call after that is local.
versionedUrl#
versionedUrl is ${url}?v=${etag}, so it changes whenever the content does. Use it for a stable path that gets overwritten: if avatars/u7.png is replaced every time the user picks a new picture, url never changes and caches keep serving the old bytes.
Pair it with cache: 'immutable' at upload. See Caching.
Private buckets#
A private bucket has no public host, so url and versionedUrl are undefined on every record. Nothing in the code declares this: the SDK learns it from the backend when it fetches credentials, and objects are stored with Cache-Control: private. Reads go through signedReadUrl().
signedReadUrl#
A time-limited URL anyone can GET. Use it on a private bucket, or for an object you do not want linked from a public page.
All three are optional.
| Option | Type | Default | Description |
|---|---|---|---|
expiresIn | Duration | '5m' | How long to ask for. '15m', '2h', or a bare number of seconds. |
downloadAs | string | none, displayed inline | Save as this filename instead of displaying inline. |
contentType | string | the stored type | What storage answers with as Content-Type, overriding what was stored. |
Use expiresAt, not expiresIn#
expiresIn is what you asked for. expiresAt is what you got, and it can be sooner, because a link cannot outlive the credential that signed it. Cache the link until expiresAt, never until a deadline you compute yourself. This applies to signedUploadUrl too. The reason is on How signing works.
downloadAs#
Sets Content-Disposition: attachment, so the browser saves the file under that name rather than rendering it. Unicode names arrive intact. The filename is signed into the URL, so it cannot be edited afterwards.
contentType#
Overrides what storage answers with, without rewriting the object. Throws invalid_input if it is not a valid media type.