跳到主要内容

PG 模式云存储

CloudBase Flutter SDK 从 1.3.0 起提供 PG 模式云存储。通过 app.storage.fromBucket(bucketId) 获取指定 Bucket 的对象客户端。路径参数都是 Bucket 内对象名,不要传 cloud:// fileID。

from() 仍是传统模式入口。JS SDK 用 app.storage.from(bucketId) 进入 PG Bucket,Flutter SDK 用 fromBucket,避免和传统模式重名。

与传统模式云存储的差异

PG 模式与传统模式 API 的关键差异:

维度PG 模式传统模式
入口app.storage.fromBucket(bucketId)app.storage.from()
路径参数Bucket 内对象名,如 user-123/avatar.png上传、复制、移动用相对路径;下载、删除、签名 URL 用 cloud:// fileID
权限模型PostgreSQL RLS(Row Level Security,行级安全策略)内置权限 + JSON 安全规则
Bucket 管理支持 createBucket / listBuckets / getBucket / updateBucket / deleteBucket不支持
文件列表支持 list()不支持
覆盖默认值upsert 默认 falseupsert 默认 true
跨 Bucket 操作move() / copy() 支持 destinationBucket不支持
下载download() 返回文件字节getDownloadUrls() / createSignedUrl()
公开 URLgetPublicUrl() 本地拼接,不发请求不支持
对象信息支持 info() / exists()不支持

详见:传统模式云存储

快速开始​

final app = await CloudBase.init(
env: 'your-env-id',
accessKey: 'your-access-key',
);

final bucket = app.storage.fromBucket('avatars');

final uploaded = await bucket.upload(
'user-123/avatar.png',
fileBytes,
options: PgStorageUploadOptions(
contentType: 'image/png',
upsert: true,
),
);
if (!uploaded.isSuccess) {
print(uploaded.error?.message);
return;
}

final file = await bucket.download('user-123/avatar.png');
final signed = await bucket.createSignedUrl('user-123/avatar.png', 3600);
await bucket.remove(['user-123/avatar.png']);

API 方法​

Bucket 客户端​

  • fromBucket() - 选择 Bucket,返回对象操作客户端

文件操作​

URL 管理​

文件信息​

Bucket 管理​


fromBucket​

CloudBasePgStorageFileApi app.storage.fromBucket(String bucketId)

参数

bucketId
String

PG Bucket ID,对应 `storage.buckets.id`。空值、长度超过 256,或含空白、`/`、`\`、`?`、`#`、`%` 时同步抛出 `StorageError`。

返回

CloudBasePgStorageFileApi
Object

PG 模式 Bucket 的对象操作客户端。

示例

final bucket = app.storage.fromBucket('avatars');

upload​

上传文件。默认不覆盖同名对象。文件字节按二进制发送。

Future<StorageResponse<StorageUploadResult>> upload(
String path,
List<int> fileData, {
PgStorageUploadOptions? options,
})

class PgStorageUploadOptions {
String? cacheControl; // 秒数字符串,默认 3600,写成 Cache-Control: max-age=<值>
String? contentType; // 未传时为 text/plain;charset=UTF-8
Map<String, dynamic>? metadata; // base64(JSON),放进 x-metadata
bool upsert; // 默认 false,仅 upload 发送 x-upsert
Map<String, String>? headers;
}

class StorageUploadResult {
String? id;
String? path;
String? fullPath; // 服务端返回的对象 Key
}

参数

path
String

Bucket 内对象名,如 `user-123/avatar.png`。不要传 `cloud://` fileID。

fileData
List<int>

文件字节。

options
PgStorageUploadOptions

上传选项。默认不覆盖同名对象。

返回

data
Object

返回数据

error
StorageError?

错误信息。含 `code`、`message`、`requestId`、`status`。

示例

final result = await app.storage.fromBucket('avatars').upload(
'user-123/avatar.png',
fileBytes,
options: PgStorageUploadOptions(
contentType: 'image/png',
upsert: true,
metadata: {'usage': 'avatar'},
),
);

if (result.isSuccess) {
print(result.data?.fullPath);
} else {
print(result.error?.message);
}

update​

覆盖已有文件。请求使用 PUT,不发送 x-upsert。

Future<StorageResponse<StorageUploadResult>> update(
String path,
List<int> fileData, {
PgStorageUploadOptions? options,
})

参数

path
String

Bucket 内对象名。

fileData
List<int>

新的文件字节。

options
PgStorageUploadOptions

上传选项。`upsert` 不会发给服务端。

返回

data
StorageUploadResult?

更新结果。

error
StorageError?

错误信息。

示例

await app.storage.fromBucket('avatars').update(
'user-123/avatar.png',
fileBytes,
options: PgStorageUploadOptions(contentType: 'image/png'),
);

download​

下载文件并返回字节。

Future<StorageResponse<List<int>>> download(
String path, {
PgStorageUrlOptions? options,
})

class PgStorageUrlOptions {
Object? download; // true 追加 download=;字符串作为下载文件名
String? cacheNonce; // 追加到 URL,用于绕过 CDN 缓存
}

参数

path
String

Bucket 内对象名。

options
PgStorageUrlOptions

下载查询参数。

返回

data
List<int>?

文件字节。

error
StorageError?

错误信息。

示例

final result = await app.storage.fromBucket('avatars').download(
'user-123/avatar.png',
options: PgStorageUrlOptions(download: 'avatar.png'),
);

if (result.isSuccess) {
print(result.data?.length);
}

remove​

删除一个或多个文件。单次最多 100 个,超出后自动分批。任一批失败,整个调用失败。

Future<StorageResponse<List<PgRemovedObject>>> remove(List<String> paths)

class PgRemovedObject {
String name;
String bucketId;
String? ownerId;
String? id;
Map<String, dynamic>? metadata;
}

参数

paths
List<String>

Bucket 内对象名列表。

返回

data
List<PgRemovedObject>?

删除成功的对象。

error
StorageError?

错误信息。

示例

await app.storage.fromBucket('avatars').remove(['user-123/avatar.png']);

move​

移动文件。源对象会被删除,可以跨 Bucket。

Future<StorageResponse<PgStorageMessage>> move(
String fromPath,
String toPath, {
PgStorageDestinationOptions? options,
})

class PgStorageDestinationOptions {
String? destinationBucket; // 不传则与源 Bucket 相同
bool? upsert; // 为 true 时允许覆盖目标路径
bool? copyMetadata; // 仅 copy 使用
Map<String, dynamic>? metadata; // 仅 copy 使用
}

class PgStorageMessage {
String message;
}

参数

fromPath
String

源对象名。

toPath
String

目标对象名。

options
PgStorageDestinationOptions

目标 Bucket 与覆盖选项。`copyMetadata` 和 `metadata` 不会发给移动接口。

返回

data
Object

返回数据

error
StorageError?

错误信息。

示例

await app.storage.fromBucket('avatars').move(
'user-123/avatar.png',
'user-123/archive/avatar.png',
);

copy​

复制文件,可以跨 Bucket。

Future<StorageResponse<PgCopyResult>> copy(
String fromPath,
String toPath, {
PgStorageDestinationOptions? options,
})

class PgCopyResult {
String path; // 服务端返回的 Key
}

参数

fromPath
String

源对象名。

toPath
String

目标对象名。

options
PgStorageDestinationOptions

跨 Bucket、覆盖和元数据选项。

返回

data
Object

返回数据

error
StorageError?

错误信息。

示例

await app.storage.fromBucket('avatars').copy(
'user-123/avatar.png',
'user-123/avatar-copy.png',
options: PgStorageDestinationOptions(
upsert: true,
copyMetadata: true,
),
);

createSignedUrl​

创建临时访问链接。

Future<StorageResponse<PgSignedUrl>> createSignedUrl(
String path,
int expiresIn, {
PgStorageUrlOptions? options,
})

class PgSignedUrl {
String fullSignedUrl;
}

参数

path
String

Bucket 内对象名。

expiresIn
int

有效期,单位秒。

options
PgStorageUrlOptions

追加到签名 URL 的 `download`、`cacheNonce`。

返回

data
Object

返回数据

error
StorageError?

错误信息。

示例

final result = await app.storage.fromBucket('avatars').createSignedUrl(
'user-123/avatar.png',
3600,
options: PgStorageUrlOptions(download: 'avatar.png'),
);

print(result.data?.fullSignedUrl);

createSignedUrls​

批量创建临时访问链接。单次最多 500 个,超出后自动分批。

Future<StorageResponse<List<PgSignedUrlItem>>> createSignedUrls(
List<String> paths,
int expiresIn, {
PgStorageUrlOptions? options,
})

class PgSignedUrlItem {
String path;
String? fullSignedUrl; // 失败时为 null
String? error;
}

参数

paths
List<String>

Bucket 内对象名列表。

expiresIn
int

有效期,单位秒。

options
PgStorageUrlOptions

追加到每条签名 URL 的选项。

返回

data
List<PgSignedUrlItem>?

批量签名结果。单项失败时 `fullSignedUrl` 为 null,`error` 为原因。

error
StorageError?

整批请求的错误信息。

示例

final result = await app.storage.fromBucket('avatars').createSignedUrls(
['user-123/avatar.png'],
3600,
);

getPublicUrl​

拼接公开对象 URL,不发请求。对象能否访问取决于 Bucket 的 public 属性。

StorageResponse<PgPublicUrl> getPublicUrl(
String path, {
PgStorageUrlOptions? options,
})

class PgPublicUrl {
String publicUrl;
}

参数

path
String

Bucket 内对象名。

options
PgStorageUrlOptions

追加到公开 URL 的 `download`、`cacheNonce`。

返回

data
Object

返回数据

示例

final result = app.storage.fromBucket('public-assets').getPublicUrl(
'logos/cloudbase.png',
);

print(result.data?.publicUrl);

createSignedUploadUrl​

创建上传签名 URL 和 token。调用时需要登录态。

Future<StorageResponse<PgSignedUploadUrl>> createSignedUploadUrl(
String path, {
bool upsert = false,
})

class PgSignedUploadUrl {
String fullSignedUrl;
String token;
String path;
}

参数

path
String

Bucket 内对象名。

upsert
bool

是否允许覆盖同名对象,默认 `false`。为 `true` 时发送 `x-upsert: true`。

返回

data
Object

返回数据

error
StorageError?

错误信息。

示例

final bucket = app.storage.fromBucket('avatars');
final signed = await bucket.createSignedUploadUrl(
'user-123/avatar.png',
upsert: true,
);

if (signed.isSuccess) {
await bucket.uploadToSignedUrl(
'user-123/avatar.png',
signed.data!.token,
fileBytes,
);
}

uploadToSignedUrl​

凭签名 token 上传,不携带登录态。

Future<StorageResponse<PgSignedUploadResult>> uploadToSignedUrl(
String path,
String token,
List<int> fileData, {
PgStorageUploadOptions? options,
})

class PgSignedUploadResult {
String path;
String fullPath;
}

参数

path
String

Bucket 内对象名,需与签名时的路径一致。

token
String

`createSignedUploadUrl` 返回的 token。

fileData
List<int>

文件字节。

options
PgStorageUploadOptions

内容类型、缓存和元数据。此方法不发送 `x-upsert`。

返回

data
Object

返回数据

error
StorageError?

错误信息。

示例

await bucket.uploadToSignedUrl(
'user-123/avatar.png',
token,
fileBytes,
options: PgStorageUploadOptions(contentType: 'image/png'),
);

info​

获取文件元信息。

Future<StorageResponse<PgObjectInfo>> info(String path)

class PgObjectInfo {
String id;
String name;
String? version;
String bucketId;
int? size;
String? contentType;
String? cacheControl;
String? etag;
Map<String, dynamic>? metadata;
String? lastModified;
String? createdAt;
}

参数

path
String

Bucket 内对象名。

返回

data
PgObjectInfo?

对象元信息。服务端 snake_case 字段已映射为 camelCase。

error
StorageError?

错误信息。

示例

final result = await app.storage.fromBucket('avatars').info('user-123/avatar.png');
print(result.data?.size);

exists​

检查文件是否存在。HTTP 400 和 404 视为不存在,data 为 false。

Future<StorageResponse<bool>> exists(String path)

参数

path
String

Bucket 内对象名。

返回

data
bool?

是否存在。

error
StorageError?

除 400、404 以外的错误。

示例

final result = await app.storage.fromBucket('avatars').exists('user-123/avatar.png');
print(result.data);

list​

按前缀列出对象。path 不传表示 Bucket 根目录。前缀保留调用方传入的形态,包括末尾 /。

Future<StorageResponse<PgListObjectsResponse>> list([
String? path,
PgStorageListOptions? options,
])

class PgStorageListOptions {
int? limit;
String? cursor; // 上一页的 nextCursor
bool? withDelimiter; // 为 true 时用 / 区分文件夹和对象
PgStorageSortBy? sortBy;
}

class PgStorageSortBy {
String? column; // name、created_at、updated_at
String? order; // asc、desc
}

class PgListObjectsResponse {
List<PgListObjectsEntry> folders;
List<PgListObjectsEntry> objects;
bool hasNext;
String? nextCursor;
}

参数

path
String?

路径前缀。不传表示 Bucket 根目录。

options
PgStorageListOptions

分页、分隔符和排序。

返回

data
Object

返回数据

error
StorageError?

错误信息。

示例

final result = await app.storage.fromBucket('avatars').list(
'user-123/',
PgStorageListOptions(limit: 20, withDelimiter: true),
);

if (result.isSuccess) {
for (final object in result.data!.objects) {
print(object.name);
}
}

Bucket 管理​

以下方法直接挂在 app.storage 上,只在 PG 模式环境可用。bucketId 的字符限制与 fromBucket 相同。

Future<StorageResponse<List<PgBucket>>> listBuckets({
PgListBucketsOptions? options,
})

Future<StorageResponse<PgBucket>> getBucket(String bucketId)

Future<StorageResponse<PgBucketName>> createBucket(
String bucketId, {
PgCreateBucketOptions? options,
})

Future<StorageResponse<PgStorageMessage>> updateBucket(
String bucketId,
PgUpdateBucketOptions options,
)

Future<StorageResponse<PgStorageMessage>> deleteBucket(String bucketId)

class PgCreateBucketOptions {
bool public; // 默认 false
String? type; // 仅创建时可传,目前为 STANDARD
Object? fileSizeLimit; // 数字为字节,字符串如 100MB,null 表示不限制
Object? allowedMimeTypes; // List<String>,null 表示不限制
}

class PgUpdateBucketOptions {
bool? public; // 未传则不更新
Object? fileSizeLimit; // 显式 null 表示取消限制
Object? allowedMimeTypes;
}

class PgListBucketsOptions {
int? limit;
int? offset;
String? sortColumn; // id、name、created_at、updated_at
String? sortOrder; // asc、desc
String? search;
}

class PgBucket {
String id;
String name;
String? ownerId;
bool public;
String? type;
int? fileSizeLimit; // 字节,null 表示不限制
List<String>? allowedMimeTypes;
String createdAt;
String updatedAt;
}

参数

bucketId
String

Bucket ID。`createBucket` 同时把它作为 `id` 和 `name`。

options
Object

创建、更新或列表参数。更新时未传的字段不会出现在请求体里,也不能修改 `type`。

返回

data
PgBucket | List<PgBucket> | PgBucketName | PgStorageMessage

Bucket 操作结果。`deleteBucket` 只删除空 Bucket,Bucket 非空时请求失败。

error
StorageError?

错误信息。

示例

final created = await app.storage.createBucket(
'avatars',
options: PgCreateBucketOptions(
public: false,
type: 'STANDARD',
fileSizeLimit: '100MB',
allowedMimeTypes: ['image/png', 'image/jpeg'],
),
);

final buckets = await app.storage.listBuckets(
options: PgListBucketsOptions(limit: 20, offset: 0),
);

await app.storage.updateBucket(
'avatars',
PgUpdateBucketOptions(public: true),
);

最佳实践​

错误处理​

请求失败时,结果在 StorageResponse.error,不会抛异常。bucketId 非法,或对象路径包含 .. 时,调用会直接抛出 StorageError。

final result = await app.storage.fromBucket('avatars').upload(
'user-123/avatar.png',
fileBytes,
);

if (!result.isSuccess) {
print(result.error?.message);
return;
}

对象路径​

先用 fromBucket(bucketId) 选定 Bucket,之后的路径都是 Bucket 内对象名。

  • 不要传 cloud:// fileID。
  • SDK 会去掉路径首尾的 /,并合并连续的 /。
  • list 的前缀不走这套清理,末尾 / 会保留。
  • 路径中的 .. 会直接抛错。

常见问题​

路径参数​

fromBucket(bucketId) 已经指定 Bucket。方法里的 path 是 Bucket 内对象名,不需要也不应传 cloud:// fileID。

访问权限​

PG 模式由 PostgreSQL storage.objects 和 storage.buckets 上的 RLS Policy 控制。返回 403 时,检查当前登录态、对象路径、bucketId 和对应策略。