Skip to main content

Access and Download Files

This guide covers file access in Postgres-Native Cloud Storage: direct downloads, signed download URLs, batch signed URLs, and public URLs.

Access methods

MethodUse casePermission requirementNotes
download()Current user reads file contentSELECT policyReturns Blob or stream
createSignedUrl()Temporarily share a private fileSELECT permission when creating the URLURL expires
createSignedUrls()Batch-generate temporary URLsSELECT on each objectUseful for list pages
getPublicUrl()Public assetsBucket and RLS allow public readBuilds URL only; no request
HTTP API downloadBackend or cross-language integrationToken or signed URLUseful for server-side flows

Direct download

const bucket = app.storage.from('private-files');

const { data: blob, error } = await bucket.download('user-123/resume.pdf');

if (error) {
throw error;
}

const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'resume.pdf';
a.click();
URL.revokeObjectURL(url);

If the runtime supports streams:

const { data: stream, error } = await bucket
.download('videos/demo.mp4')
.asStream();

if (error) {
throw error;
}

Signed download URL

Use createSignedUrl() to create a temporary URL for private files:

const { data, error } = await bucket.createSignedUrl('user-123/resume.pdf', 600, {
download: 'resume.pdf',
});

if (error) {
throw error;
}

console.log(data.fullSignedURL);

The second parameter is the expiration time in seconds.

Signed URLs are for temporary sharing. For long-lived public assets, use public-read policies and getPublicUrl().

Batch signed URLs

const { data, error } = await bucket.createSignedUrls([
'user-123/a.png',
'user-123/b.png',
], 600);

if (error) {
throw error;
}

console.log(data);

Every object is still checked by the SELECT policy. Batch signing does not bypass RLS.

Public URL

const { data } = app.storage
.from('public-assets')
.getPublicUrl('logos/cloudbase.png');

console.log(data.publicUrl);

getPublicUrl() only builds the URL. Whether it can be accessed depends on:

  1. the bucket's public-resource configuration;
  2. a public-read SELECT policy on storage.objects;
  3. the object existing.

Public / Private recommendations

ScenarioRecommended methodNotes
Logo, public banner, public article imagegetPublicUrl()Good for long-lived public/cacheable assets
Private user attachmentdownload()Current user reads their own file
Temporary private sharingcreateSignedUrl()Short-lived external access
Private list thumbnailscreateSignedUrls()Batch-generate short-lived links
Server-side download proxyHTTP APICentralized auth, audit, and rate limiting

CDN acceleration

For public assets that need better performance and cache hit rates, enable CDN acceleration. The CDN behavior of Postgres-Native Cloud Storage is documented separately. See CDN Acceleration and Caching.

RLS and access operations

Read, download, signing, and listing may all use SELECT, but the business intent can differ:

SDK operationDMLTypical permission goal
download()SELECTRead file content
createSignedUrl()SELECTCreate a temporary access URL
createSignedUrls()SELECTCreate temporary URLs in batch
getPublicUrl()May not request the serverAccess is decided when the URL is used
list()SELECTList folders or objects
info() / exists()SELECTRead metadata or existence

If you need to allow access to a known object but forbid directory listing, use storage.operation(), storage.allow_only_operation(), or storage.allow_any_operation(). See Permission management.

Cache and overwrites

Public assets are often cached by browsers or CDNs. If you overwrite the same path, users may temporarily see old content. For details, see CDN Acceleration and Caching: Overwrites and CDN cache.

Troubleshooting

SymptomLikely causeWhat to check
download() returns 403SELECT policy does not allow itPath, bucket_id, current auth.uid()
Failed to create signed URLCurrent user cannot read the objectQuery metadata and re-check RLS
Public URL cannot be accessedURL exists, but no public-read policyAdd SELECT TO anon for public resources
Old file is returnedCache has not refreshedUse a new path or URL version parameter

AI-friendly prompt

Is the file public: no
Viewer: file owner or team member
Access method: create a signed download URL valid for 10 minutes
List directory: not allowed; only known object access
Path template: <uid>/<filename>