概述
快速开始
import cloudbase from "@cloudbase/js-sdk";
const app = cloudbase.init({
env: "your-env-id",
});
// 使用 Supabase 风格的 API
const storage = app.storage.from();
// 上传文件
const { data, error } = await storage.upload("images/photo.jpg", file);
// 下载文件
const { data: blob } = await storage.download(
"cloud://envId.xxx/images/photo.jpg"
);
// 创建签名 URL
const {
data: { signedUrl },
} = await storage.createSignedUrl(
"[images/photo.jpg](cloud://envId.xxx/images/photo.jpg)",
3600
);
// 删除文件
await storage.remove([
"[images/photo.jpg](cloud://envId.xxx/images/photo.jpg)",
]);
API 方法
链式调用
- from() - 指定存储桶
- throwOnError() - 错误时抛出异常
文件操作
URL 管理
- createSignedUrl() - 创建签名 URL
- createSignedUrls() - 批量创建签名 URL
- getPublicUrl() - 获取公开 URL
- createSignedUploadUrl() - 创建上传签名 URL
文件信息
from
from(bucket?: string): this
指定要操作的存储桶,默认选中环境云存储桶。
示例:
const storage = app.storage.from();
throwOnError
throwOnError(): this
设置在发生错误时抛出异常,而不是返回错误对象。
示例:
const storage = app.storage.from().throwOnError();
try {
const { data } = await storage.upload("file.txt", file);
console.log("上传成功:", data);
} catch (error) {
console.error("上传失败:", error);
}
upload
upload(
path: string,
fileBody: FileBody,
fileOptions?: FileOptions
): Promise<{ data: { id: string; path: string; fullPath: string }; error: null } | { data: null; error: StorageError }>
上传文件到云存储。
参数
文件路径,如 'images/photo.jpg'
文件内容,支持 File、Blob、ArrayBuffer、Buffer、Stream 等类型
上传选项
返回
示例
// 上传文件
const { data, error } = await app.storage
.from()
.upload("images/photo.jpg", file);
if (error) {
console.error("上传失败:", error);
} else {
console.log("上传成功:", data);
console.log("文件 ID:", data.id);
console.log("文件路径:", data.path);
}
// 上传文件并设置选项
const { data, error } = await app.storage
.from()
.upload("images/photo.jpg", file, {
cacheControl: "max-age=3600",
contentType: "image/jpeg",
metadata: {
author: "John Doe",
uploadedAt: new Date().toISOString(),
},
});
if (error) {
console.error("上传失败:", error);
} else {
console.log("上传成功:", data);
}
update
update(
path: string,
fileBody: FileBody,
fileOptions?: FileOptions
): Promise<{ data: { id: string; path: string; fullPath: string }; error: null } | { data: null; error: StorageError }>
更新已存在的文件。
参数
文件路径
新的文件内容
上传选项
返回
示例
// 更新文件内容
const { data, error } = await app.storage
.from()
.update("images/photo.jpg", newFile);
if (error) {
console.error("更新失败:", error);
} else {
console.log("更新成功:", data);
}
// 更新文件并修改元数据
const { data, error } = await app.storage
.from()
.update("images/photo.jpg", newFile, {
cacheControl: "max-age=7200",
metadata: {
updatedAt: new Date().toISOString(),
version: "2.0",
},
});
if (error) {
console.error("更新失败:", error);
} else {
console.log("文件已更新:", data.path);
}
download
download(
fileId: string,
options?: TransformOptions
): Promise<{ data: Blob; error: null } | { data: null; error: StorageError }>
下载文件并返回 Blob 对象。支持图片转换功能。
参数
文件 ID
图片转换选项(仅对图片有效)
返回
示例
// 下载原始文件
const { data, error } = await app.storage
.from()
.download("cloud://envId.xxx/images/photo.jpg");
if (data) {
// 创建下载链接
const url = URL.createObjectURL(data);
const a = document.createElement("a");
a.href = url;
a.download = "photo.jpg";
a.click();
}
// 下载并转换图片为缩略图
const { data: thumbnail, error } = await app.storage
.from()
.download("cloud://envId.xxx/images/photo.jpg", {
width: 300,
height: 200,
quality: 80,
});
if (thumbnail) {
// 显示缩略图
const url = URL.createObjectURL(thumbnail);
document.getElementById("thumbnail").src = url;
}
// 下载并转换图片格式
const { data: webpImage, error } = await app.storage
.from()
.download("cloud://envId.xxx/images/photo.jpg", {
format: "webp",
quality: 90,
});
if (webpImage) {
console.log("已转换为 WebP 格式");
}
remove
remove(
fileIds: string[]
): Promise<{ data: { id: string }[]; error: null } | { data: null; error: StorageError }>
删除一个或多个文件。
参数
要删除的文件 ID数组
返回
示例
// 删除单个文件
const { data, error } = await app.storage
.from()
.remove(["cloud://envId.xxx/images/photo.jpg"]);
if (error) {
console.error("删除失败:", error);
} else {
console.log("删除成功:", data);
}
// 批量删除多个文件
const { data, error } = await app.storage
.from()
.remove([
"cloud://envId.xxx/images/photo1.jpg",
"cloud://envId.xxx/images/photo2.jpg",
"cloud://envId.xxx/documents/file.pdf",
]);
if (error) {
console.error("删除失败:", error);
} else {
console.log(`成功删除 ${data.length} 个文件`);
}
move
move(
fromPath: string,
toPath: string
): Promise<{ data: { message: string }; error: null } | { data: null; error: StorageError }>
移动文件到新位置。
参数
源文件路径
目标文件路径
返回
示例
// 移动文件到新位置
const { data, error } = await app.storage
.from()
.move("images/old-photo.jpg", "images/archive/photo.jpg");
if (error) {
console.error("移动失败:", error);
} else {
console.log("移动成功:", data.message);
}
// 重命名文件(移动到同一目录下的新名称)
const { data, error } = await app.storage
.from()
.move("images/photo.jpg", "images/new-photo.jpg");
if (error) {
console.error("重命名失败:", error);
} else {
console.log("重命名成功");
}
copy
copy(
fromPath: string,
toPath: string
): Promise<{ data: { path: string }; error: null } | { data: null; error: StorageError }>
复制文件到新位置。
参数
源文件路径
目标文件路径
返回
示例
// 复制文件到新位置
const { data, error } = await app.storage
.from()
.copy("images/photo.jpg", "images/backup/photo.jpg");
if (error) {
console.error("复制失败:", error);
} else {
console.log("复制成功,新文件路径:", data.path);
}
// 在同一目录下创建副本
const { data, error } = await app.storage
.from()
.copy("images/photo.jpg", "images/photo-copy.jpg");
if (error) {
console.error("创建副本失败:", error);
} else {
console.log("副本已创建:", data.path);
}
createSignedUrl
createSignedUrl(
fileId: string,
expiresIn: number,
options?: TransformOptions
): Promise<{ data: { signedUrl: string }; error: null } | { data: null; error: StorageError }>
创建一个带签名的临时访问 URL。
参数
文件 ID
URL 有效期(秒)
图片转换选项
返回
示例
// 创建 1 小时有效的临时链接
const { data, error } = await app.storage
.from()
.createSignedUrl("cloud://envId.xxx/images/photo.jpg", 3600);
if (error) {
console.error("创建失败:", error);
} else {
console.log("临时链接:", data.signedUrl);
// 可以直接使用这个链接访问文件
}
// 创建缩略图的临时链接
const { data, error } = await app.storage
.from()
.createSignedUrl("cloud://envId.xxx/images/photo.jpg", 3600, {
width: 300,
height: 200,
quality: 80,
});
if (error) {
console.error("创建失败:", error);
} else {
// 使用缩略图链接
document.getElementById("thumbnail").src = data.signedUrl;
}
// 创建 5 分钟有效的短期分享链接
const { data, error } = await app.storage
.from()
.createSignedUrl("cloud://envId.xxx/images/photo.jpg", 300);
if (error) {
console.error("创建失败:", error);
} else {
// 分享这个链接,5 分钟后自动失效
navigator.clipboard.writeText(data.signedUrl);
alert("分享链接已复制到剪贴板");
}
createSignedUrls
createSignedUrls(
fileIds: string[],
expiresIn: number,
): Promise<{ data: Array<{ path: string; signedUrl: string }>; error: null } | { data: null; error: StorageError }>
批量创建带签名的临时访问 URL。
参数
文件 ID数组
URL 有效期(秒)
返回
示例
// 批量创建多个文件的临时链接
const { data, error } = await app.storage
.from()
.createSignedUrls(
[
"cloud://envId.xxx/images/photo1.jpg",
"cloud://envId.xxx/images/photo2.jpg",
"cloud://envId.xxx/images/photo3.jpg",
],
3600
);
if (error) {
console.error("创建失败:", error);
} else {
data.forEach((item) => {
console.log(`${item.path}: ${item.signedUrl}`);
});
}
// 批量创建缩略图链接
const { data, error } = await app.storage
.from()
.createSignedUrls(
[
"cloud://envId.xxx/images/photo1.jpg",
"cloud://envId.xxx/images/photo2.jpg",
"cloud://envId.xxx/images/photo3.jpg",
],
3600
);
if (error) {
console.error("创建失败:", error);
} else {
// 显示所有缩略图
const gallery = document.getElementById("gallery");
data.forEach((item) => {
const img = document.createElement("img");
img.src = item.signedUrl;
gallery.appendChild(img);
});
}
getPublicUrl
getPublicUrl(
fileId: string,
options?: TransformOptions
): { data: { publicUrl: string } }
获取文件的公开访问 URL(不带签名)。
参数
文件 ID
图片转换选项
返回
示例
// 获取文件的公开访问链接
const { data } = await app.storage
.from()
.getPublicUrl("cloud://envId.xxx/images/photo.jpg");
console.log("公开链接:", data.publicUrl);
// 可以直接使用这个链接(如果文件设置为公开访问)
// 获取缩略图的公开链接
const { data } = await app.storage
.from()
.getPublicUrl("cloud://envId.xxx/images/photo.jpg", {
width: 300,
height: 200,
quality: 80,
});
// 在 img 标签中使用
document.getElementById("thumbnail").src = data.publicUrl;
info
info(
fileId: string
): Promise<{ data: FileInfo; error: null } | { data: null; error: StorageError }>
获取文件的详细信息。
参数
文件 ID
返回
示例
// 获取文件详细信息
const { data, error } = await app.storage
.from()
.info("cloud://envId.xxx/images/photo.jpg");
if (error) {
console.error("获取失败:", error);
} else {
console.log("文件名:", data.name);
console.log("文件大小:", data.size, "字节");
console.log("创建时间:", data.created_at);
console.log("更新时间:", data.updated_at);
console.log("元数据:", data.metadata);
}
// 在界面上显示文件详情
const { data, error } = await app.storage
.from()
.info("cloud://envId.xxx/documents/report.pdf");
if (data) {
const sizeInMB = (data.size / 1024 / 1024).toFixed(2);
document.getElementById("fileName").textContent = data.name;
document.getElementById("fileSize").textContent = `${sizeInMB} MB`;
document.getElementById("createdAt").textContent = new Date(
data.created_at
).toLocaleString();
}
exists
exists(
fileId: string
): Promise<{ data: boolean; error: null } | { data: null; error: StorageError }>
检查文件是否存在。
参数
文件 ID
返回
示例
// 检查文件是否存在
const { data: exists, error } = await app.storage
.from()
.exists("cloud://envId.xxx/images/photo.jpg");
if (error) {
console.error("检查失败:", error);
} else if (exists) {
console.log("文件存在");
} else {
console.log("文件不存在");
}
// 上传前检查文件是否已存在
const { data: exists } = await app.storage
.from()
.exists("cloud://envId.xxx/images/photo.jpg");
if (exists) {
// 文件已存在,询问是否覆盖
const shouldOverwrite = confirm("文件已存在,是否覆盖?");
if (shouldOverwrite) {
await app.storage.from().update("images/photo.jpg", file);
}
} else {
// 文件不存在,直接上传
await app.storage.from().upload("images/photo.jpg", file);
}
createSignedUploadUrl
createSignedUploadUrl(
fileId: string,
options?: { upsert?: boolean }
): Promise<{ data: { signedUrl: string; path: string; token: string }; error: null } | { data: null; error: StorageError }>
创建一个用于上传的签名 URL,允许客户端直接上传文件到云存储。
参数
文件 ID
上传选项
返回
示例
// 创建用于上传的签名 URL
const { data, error } = await app.storage
.from()
.createSignedUploadUrl("cloud://envId.xxx/images/photo.jpg");
if (error) {
console.error("创建失败:", error);
} else {
console.log("上传 URL:", data.signedUrl);
console.log("上传令牌:", data.token);
// 使用这个 URL 直接上传文件
const formData = new FormData();
formData.append("file", file);
await fetch(data.signedUrl, {
method: "PUT",
body: file,
headers: {
"Content-Type": file.type,
},
});
}
// 在客户端实现直传功能
async function uploadFileDirectly(file) {
// 1. 获取签名上传 URL
const { data, error } = await app.storage
.from()
.createSignedUploadUrl(`cloud://envId.xxx/uploads/${file.name}`);
if (error) {
console.error("获取上传 URL 失败:", error);
return;
}
// 2. 直接上传到云存储
const uploadResponse = await fetch(data.signedUrl, {
method: "PUT",
body: file,
headers: {
"Content-Type": file.type,
},
});
if (uploadResponse.ok) {
console.log("上传成功");
} else {
console.error("上传失败");
}
}
类型定义
FileBody
type FileBody = File | Blob | ArrayBuffer | Buffer | ReadableStream | string;
支持的文件内容类型。
FileOptions
interface FileOptions {
cacheControl?: string; // 缓存控制,如 'max-age=3600'
contentType?: string; // 文件 MIME 类型,如 'image/jpeg'
metadata?: Record<string, any>; // 自定义元数据
upsert?: boolean; // 是否覆盖已存在的文件
}
文件上传选项。
TransformOptions
interface TransformOptions {
width?: number; // 目标宽度
height?: number; // 目标高度
quality?: number; // 图片质量 (1-100)
format?: "jpg" | "png" | "webp"; // 输出格式
}
图片转换选项(仅对图片有效)。
FileInfo
interface FileInfo {
name: string; // 文件名
id: string; // 文件 ID
size: number; // 文件大小(字节)
created_at: string; // 创建时间
updated_at: string; // 更新时间
metadata?: Record<string, any>; // 自定义元数据
}
文件信息对象。
StorageError
interface StorageError {
message: string; // 错误消息
statusCode?: number; // HTTP 状态码
}
存储错误对象。
迁移指南
如果你正在使用传统的 CloudBase 云存储 API,建议迁移到 Supabase 风格的 API。以下是迁移对照表:
API 对照表
| 传统 API | Supabase 风格 API | 说明 |
|---|---|---|
app.uploadFile() | app.storage.from().upload() | 上传文件 |
app.getTempFileURL() | app.storage.from().createSignedUrl() | 获取临时链接 |
app.deleteFile() | app.storage.from().remove() | 删除文件 |
app.downloadFile() | app.storage.from().download() | 下载文件 |
迁移示例
上传文件
// 传统方式
const result = await app.uploadFile({
cloudPath: "images/photo.jpg",
filePath: file,
});
console.log("文件 ID:", result.fileID);
// Supabase 风格
const { data, error } = await app.storage
.from()
.upload("images/photo.jpg", file);
if (error) {
console.error("上传失败:", error);
} else {
console.log("文件 ID:", data.id);
}
获取临时链接
// 传统方式
const res = await app.getTempFileURL({
fileList: [
{
fileID: "cloud://envId.xxx/images/photo.jpg",
maxAge: 3600,
},
],
});
const url = res.fileList[0].tempFileURL;
// Supabase 风格
const { data, error } = await app.storage
.from()
.createSignedUrl("cloud://envId.xxx/images/photo.jpg", 3600);
if (data) {
const url = data.signedUrl;
}
删除文件
// 传统方式
const result = await app.deleteFile({
fileList: ["cloud://xxx.jpg"],
});
// Supabase 风格
const { data, error } = await app.storage
.from()
.remove(["cloud://envId.xxx/images/photo.jpg"]);
下载文件
// 传统方式
const result = await app.downloadFile({
fileID: "cloud://xxx.jpg",
});
// Supabase 风格
const { data: blob, error } = await app.storage
.from()
.download("cloud://envId.xxx/images/photo.jpg");
if (blob) {
// 使用 Blob 对象
const url = URL.createObjectURL(blob);
}
最佳实践
1. 错误处理
始终检查错误对象:
const { data, error } = await app.storage.from().upload("file.txt", file);
if (error) {
console.error("操作失败:", error.message);
// 处理错误
return;
}
// 使用 data
console.log("操作成功:", data);
或者使用 throwOnError() 方法:
try {
const { data } = await app.storage
.from()
.throwOnError()
.upload("file.txt", file);
console.log("操作成功:", data);
} catch (error) {
console.error("操作失败:", error);
}
2. 文件路径规范
- 使用
/分隔目录 - 不要以
/开头 - 使用有意义的目录结构
// ✅ 推荐
"images/avatars/user-123.jpg";
"documents/reports/2024/report.pdf";
"uploads/temp/file-abc.txt";
// ❌ 不推荐
"/images/photo.jpg"; // 不要以 / 开头
"photo.jpg"; // 缺少目录结构
3. 图片优化
使用图片转换选项优化加载速度:
// 获取缩略图
const { data } = await app.storage
.from()
.createSignedUrl("images/large-photo.jpg", 3600, {
width: 300,
height: 300,
quality: 80,
format: "webp", // 使用 WebP 格式减小文件大小
});
4. 批量操作
对于多个文件的操作,使用批量方法提高效率:
// ✅ 推荐:批量创建签名 URL
const { data } = await app.storage
.from()
.createSignedUrls(["image1.jpg", "image2.jpg", "image3.jpg"], 3600);
// ❌ 不推荐:逐个创建
for (const path of paths) {
await app.storage.from().createSignedUrl(path, 3600);
}
5. 缓存控制
合理设置缓存控制头:
// 静态资源:长期缓存
await app.storage.from().upload("images/logo.png", file, {
cacheControl: "max-age=31536000", // 1 年
});
// 动态内容:短期缓存
await app.storage.from().upload("data/report.json", file, {
cacheControl: "max-age=300", // 5 分钟
});
// 不缓存
await app.storage.from().upload("temp/data.txt", file, {
cacheControl: "no-cache",
});
常见问题
1. 如何处理大文件上传?
对于大文件,建议使用 createSignedUploadUrl() 实现客户端直传:
const { data } = await app.storage
.from()
.createSignedUploadUrl("large-file.zip");
// 使用 fetch 上传大文件
await fetch(data.signedUrl, {
method: "PUT",
body: largeFile,
});
2. 如何实现断点续传?
CloudBase 云存储暂不直接支持断点续传,建议将大文件分片上传:
async function uploadLargeFile(file, chunkSize = 5 * 1024 * 1024) {
const chunks = Math.ceil(file.size / chunkSize);
for (let i = 0; i < chunks; i++) {
const start = i * chunkSize;
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
await app.storage.from().upload(`temp/file-${i}`, chunk);
}
// 合并分片(需要云函数支持)
}
3. 如何设置文件访问权限?
文件访问权限需要在云开发控制台的"权限控制/策略管理"中配置。
4. 支持哪些图片格式转换?
支持 JPG、PNG、WebP 格式之间的相互转换。
5. 临时链接的最长有效期是多久?
建议不超过 7 天(604800 秒)。