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
| Method | Use case | Permission requirement | Notes |
|---|---|---|---|
download() | Current user reads file content | SELECT policy | Returns Blob or stream |
createSignedUrl() | Temporarily share a private file | SELECT permission when creating the URL | URL expires |
createSignedUrls() | Batch-generate temporary URLs | SELECT on each object | Useful for list pages |
getPublicUrl() | Public assets | Bucket and RLS allow public read | Builds URL only; no request |
| HTTP API download | Backend or cross-language integration | Token or signed URL | Useful 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:
- the bucket's public-resource configuration;
- a public-read
SELECTpolicy onstorage.objects; - the object existing.
Public / Private recommendations
| Scenario | Recommended method | Notes |
|---|---|---|
| Logo, public banner, public article image | getPublicUrl() | Good for long-lived public/cacheable assets |
| Private user attachment | download() | Current user reads their own file |
| Temporary private sharing | createSignedUrl() | Short-lived external access |
| Private list thumbnails | createSignedUrls() | Batch-generate short-lived links |
| Server-side download proxy | HTTP API | Centralized 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 operation | DML | Typical permission goal |
|---|---|---|
download() | SELECT | Read file content |
createSignedUrl() | SELECT | Create a temporary access URL |
createSignedUrls() | SELECT | Create temporary URLs in batch |
getPublicUrl() | May not request the server | Access is decided when the URL is used |
list() | SELECT | List folders or objects |
info() / exists() | SELECT | Read 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
| Symptom | Likely cause | What to check |
|---|---|---|
download() returns 403 | SELECT policy does not allow it | Path, bucket_id, current auth.uid() |
| Failed to create signed URL | Current user cannot read the object | Query metadata and re-check RLS |
| Public URL cannot be accessed | URL exists, but no public-read policy | Add SELECT TO anon for public resources |
| Old file is returned | Cache has not refreshed | Use 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>