Bun Expo Updates Server 提供了一个灵活的对象存储服务 (OSS) 适配器系统,用于管理更新文件的存储和分发。本页面将详细介绍如何配置和使用这些适配器。
OSS 适配器系统允许服务器与各种云存储提供商集成,包括:
这种灵活的架构使您可以选择最适合您需求和地区的存储服务。
首先,您需要设置 OSS 提供商的配置:
// src/config/oss-config.ts
import { OSSConfig } from '../utils/oss-provider/types';
export const dogecloudConfig: OSSConfig = {
provider: 'dogecloud',
accessKey: 'your_access_key',
secretKey: 'your_secret_key'
};或者,您可以使用环境变量进行配置:
# 基本 OSS 配置
OSS_PROVIDER=dogecloud
OSS_ACCESS_KEY=your_access_key
OSS_SECRET_KEY=your_secret_key
# 根据提供商可能需要额外配置
OSS_REGION=your_region
OSS_BUCKET=your_bucket_name
OSS_ENDPOINT=your_endpoint_url # S3或自定义提供商需要
OSS_FORCE_PATH_STYLE=0 # 可选:强制路径样式 (1 表示是,0 表示否)import { getOSSProvider } from '../utils/oss-provider/factory';
async function initApp() {
try {
// 初始化 OSS 提供商
const ossProvider = await getOSSProvider();
// 检查是否成功初始化
if (!ossProvider) {
console.error('无法初始化 OSS 提供商');
return;
}
console.log(`成功初始化 ${ossProvider.getBucketName()} OSS 提供商`);
// 现在您可以使用 ossProvider 进行文件操作
} catch (error) {
console.error('初始化 OSS 时出错:', error);
}
}// 配置文件方式
const dogecloudConfig = {
provider: 'dogecloud',
accessKey: 'your_access_key',
secretKey: 'your_secret_key'
};
// 环境变量方式
// OSS_PROVIDER=dogecloud
// OSS_ACCESS_KEY=your_access_key
// OSS_SECRET_KEY=your_secret_keyconst qiniuConfig = {
provider: 'qiniu',
accessKey: 'your_access_key',
secretKey: 'your_secret_key',
region: 'your_region',
bucket: 'your_bucket'
};
// 环境变量方式
// OSS_PROVIDER=qiniu
// OSS_ACCESS_KEY=your_access_key
// OSS_SECRET_KEY=your_secret_key
// OSS_REGION=your_region
// OSS_BUCKET=your_bucketconst s3Config = {
provider: 's3',
accessKey: 'your_access_key',
secretKey: 'your_secret_key',
region: 'your_region',
bucket: 'your_bucket',
endpoint: 'your_endpoint',
forcePathStyle: false // 可选,默认为 false
};
// 环境变量方式
// OSS_PROVIDER=s3
// OSS_ACCESS_KEY=your_access_key
// OSS_SECRET_KEY=your_secret_key
// OSS_REGION=your_region
// OSS_BUCKET=your_bucket
// OSS_ENDPOINT=your_endpoint
// OSS_FORCE_PATH_STYLE=0async function uploadFile(ossProvider, localFilePath, ossFilePath) {
try {
// 读取本地文件
const fileContent = await Bun.file(localFilePath).arrayBuffer();
const content = new Uint8Array(fileContent);
// 确定 MIME 类型
const mimeType = getMimeType(localFilePath);
// 上传到 OSS
await ossProvider.putObject({
key: ossFilePath,
body: content,
contentType: mimeType
});
console.log(`成功上传 ${localFilePath} 到 ${ossFilePath}`);
} catch (error) {
console.error(`上传文件失败: ${error.message}`);
}
}async function downloadFile(ossProvider, ossFilePath, localFilePath) {
try {
// 从 OSS 获取文件
const result = await ossProvider.getObject({ key: ossFilePath });
// 将文件内容写入本地文件
await Bun.write(localFilePath, result.body);
console.log(`成功下载 ${ossFilePath} 到 ${localFilePath}`);
} catch (error) {
console.error(`下载文件失败: ${error.message}`);
}
}async function listFiles(ossProvider, prefix) {
try {
// 列出具有特定前缀的对象
const result = await ossProvider.listObjects({ prefix });
console.log(`找到 ${result.Contents.length} 个对象,前缀为 "${prefix}"`);
// 处理结果
for (const object of result.Contents) {
console.log(`- ${object.Key} (大小: ${object.Size} 字节)`);
}
} catch (error) {
console.error(`列出文件失败: ${error.message}`);
}
}async function deleteFile(ossProvider, ossFilePath) {
try {
// 删除文件
await ossProvider.deleteObject({ key: ossFilePath });
console.log(`成功删除 ${ossFilePath}`);
} catch (error) {
console.error(`删除文件失败: ${error.message}`);
}
}如果您需要集成不直接支持的存储提供商,可以创建自定义适配器:
import { OSSAdapter, OSSConfig, PutObjectParams, GetObjectParams, HeadObjectParams } from '../utils/oss-provider/types';
class CustomOSSAdapter implements OSSAdapter {
private config: OSSConfig;
private client: any; // 您的存储客户端实例
constructor(config: OSSConfig) {
this.config = config;
// 初始化您的存储客户端
this.client = new YourStorageClient({
accessKey: config.accessKey,
secretKey: config.secretKey,
// 其他配置...
});
}
getBucketName(): string {
return this.config.bucket || 'default-bucket';
}
async listObjects(params: { prefix?: string }): Promise<any> {
// 实现列出对象的逻辑
const result = await this.client.listObjects({
bucket: this.getBucketName(),
prefix: params.prefix || ''
});
return {
Contents: result.objects.map(obj => ({
Key: obj.key,
Size: obj.size
}))
};
}
async getObject(params: GetObjectParams): Promise<any> {
// 实现获取对象的逻辑
const result = await this.client.getObject({
bucket: this.getBucketName(),
key: params.key
});
return {
body: result.content,
contentType: result.contentType
};
}
async putObject(params: PutObjectParams): Promise<void> {
// 实现上传对象的逻辑
await this.client.putObject({
bucket: this.getBucketName(),
key: params.key,
content: params.body,
contentType: params.contentType
});
}
async deleteObject(params: { key: string }): Promise<void> {
// 实现删除对象的逻辑
await this.client.deleteObject({
bucket: this.getBucketName(),
key: params.key
});
}
async headObject(params: HeadObjectParams): Promise<any> {
// 实现检查对象的逻辑
const result = await this.client.headObject({
bucket: this.getBucketName(),
key: params.key
});
return {
ContentType: result.contentType,
ContentLength: result.contentLength
};
}
async generatePresignedUrl(params: { key: string; expires?: number }): Promise<string> {
// 实现生成预签名 URL 的逻辑
return this.client.generatePresignedUrl({
bucket: this.getBucketName(),
key: params.key,
expires: params.expires || 3600
});
}
}修改工厂文件以包含您的自定义适配器:
// src/utils/oss-provider/factory.ts
import { OSSAdapter, OSSConfig } from './types';
import { DogeCloudAdapter } from './dogecloud-adapter';
import { S3Adapter } from './s3-adapter';
import { CustomOSSAdapter } from './custom-adapter';
export function createOSSAdapter(config: OSSConfig): OSSAdapter {
switch (config.provider) {
case 'dogecloud':
return new DogeCloudAdapter(config);
case 's3':
return new S3Adapter(config);
case 'custom':
return new CustomOSSAdapter(config);
default:
throw new Error(`不支持的 OSS 提供商: ${config.provider}`);
}
}在使用 OSS 适配器时实施健壮的错误处理:
async function safeOSSOperation(operation, fallback) {
try {
return await operation();
} catch (error) {
console.error(`OSS 操作失败: ${error.message}`);
// 根据错误类型实施不同的重试策略
if (error.code === 'NetworkError') {
// 网络错误重试
return await retryWithBackoff(operation);
}
// 如果提供了回退值,则返回
if (fallback !== undefined) {
return fallback;
}
// 重新抛出错误以由调用者处理
throw error;
}
}连接超时
访问被拒绝错误
"找不到文件"错误
MIME 类型问题
getMimeType 工具函数确保正确的内容类型启用详细日志记录:
DEBUG=true LOG_LANGUAGE=zh-CN bun run dev检查 OSS 提供商状态:
测试连接:
async function testOSSConnection(ossProvider) {
try {
// 尝试列出一个对象来测试连接
await ossProvider.listObjects({ prefix: '', maxKeys: 1 });
console.log('OSS 连接测试成功!');
return true;
} catch (error) {
console.error('OSS 连接测试失败:', error.message);
return false;
}
}