535 lines
15 KiB
Dart
535 lines
15 KiB
Dart
import 'dart:typed_data';
|
||
|
||
import 'package:yx_oss/yx_oss.dart';
|
||
|
||
/// YX OSS 使用示例
|
||
///
|
||
/// 展示如何使用不同的认证和配置方式
|
||
void main() async {
|
||
print('YX OSS 使用示例');
|
||
|
||
// 示例1: 静态配置 + 静态认证
|
||
await example1StaticAuth();
|
||
|
||
// 示例2: STS临时凭证认证
|
||
await example2STSAuth();
|
||
|
||
// 示例3: 预签名URL认证
|
||
await example3PreSignedAuth();
|
||
|
||
// 示例4: 动态配置
|
||
await example4DynamicConfig();
|
||
|
||
// 示例5: 分片上传
|
||
await example5MultipartUpload();
|
||
}
|
||
|
||
/// 示例1: 静态配置 + 静态认证
|
||
/// 适用场景: 开发测试环境,简单的应用场景
|
||
Future<void> example1StaticAuth() async {
|
||
print('\n=== 示例1: 静态配置 + 静态认证 ===');
|
||
|
||
// 创建静态认证提供者
|
||
const authProvider = StaticAuthProvider(
|
||
accessKeyId: 'your_access_key_id',
|
||
accessKeySecret: 'your_access_key_secret',
|
||
);
|
||
|
||
// 创建静态配置提供者
|
||
const configProvider = StaticConfigProvider(
|
||
endpoint: 'oss-cn-hangzhou.aliyuncs.com',
|
||
bucketName: 'your-bucket-name',
|
||
directory: 'uploads', // 可选,默认为'uploads'
|
||
domain: 'https://your-custom-domain.com', // 可选,自定义域名
|
||
);
|
||
|
||
// 创建OSS客户端配置
|
||
const config = YxOSSConfig(
|
||
authProvider: authProvider,
|
||
configProvider: configProvider,
|
||
// 可选的超时配置
|
||
timeoutConfig: TimeoutConfig(
|
||
connectTimeout: 60000,
|
||
sendTimeout: 300000,
|
||
receiveTimeout: 300000,
|
||
),
|
||
// 可选的重试配置
|
||
retryConfig: RetryConfig(
|
||
maxRetries: 3,
|
||
initialInterval: 1000,
|
||
enableExponentialBackoff: true,
|
||
),
|
||
// 可选的日志配置
|
||
logConfig: LogConfig(
|
||
enabled: true,
|
||
level: LogLevel.info,
|
||
logRequest: false,
|
||
logResponse: false,
|
||
logError: true,
|
||
),
|
||
);
|
||
|
||
// 创建OSS客户端
|
||
final client = YxOSSClient(config);
|
||
|
||
try {
|
||
// 初始化客户端
|
||
await client.initialize();
|
||
|
||
// 准备上传的文件数据
|
||
final fileData = Uint8List.fromList('Hello, YX OSS!'.codeUnits);
|
||
|
||
// 创建上传选项
|
||
final options = UploadOptions(
|
||
overwrite: true,
|
||
acl: ACLMode.publicRead,
|
||
storageClass: StorageClass.standard,
|
||
contentType: 'text/plain',
|
||
metadata: {'author': 'YX OSS Example'},
|
||
callbacks: UploadCallbacks(
|
||
onStart: () => print('开始上传...'),
|
||
onProgress: (sent, total) {
|
||
final progress = (sent / total * 100).toStringAsFixed(1);
|
||
print('上传进度: $progress% ($sent/$total)');
|
||
},
|
||
onSuccess: (result) => print('上传成功: ${result.url}'),
|
||
onError: (error) => print('上传失败: $error'),
|
||
onComplete: () => print('上传完成'),
|
||
),
|
||
);
|
||
|
||
// 执行上传
|
||
final result = await client.uploadBytes(
|
||
fileData,
|
||
'examples/test-file.txt',
|
||
options: options,
|
||
);
|
||
|
||
print('上传结果:');
|
||
print(' URL: ${result.url}');
|
||
print(' 对象键: ${result.objectKey}');
|
||
print(' 文件大小: ${result.fileSize} 字节');
|
||
print(' ETag: ${result.etag}');
|
||
print(' 上传耗时: ${result.uploadDuration} 毫秒');
|
||
} catch (e) {
|
||
print('操作失败: $e');
|
||
} finally {
|
||
// 释放资源
|
||
client.dispose();
|
||
}
|
||
}
|
||
|
||
/// 示例2: STS临时凭证认证
|
||
/// 适用场景: 生产环境,需要动态权限控制
|
||
Future<void> example2STSAuth() async {
|
||
print('\n=== 示例2: STS临时凭证认证 ===');
|
||
|
||
// 模拟从服务器获取STS凭证的函数
|
||
Future<STSCredentials> getSTSCredentials() async {
|
||
// 实际应用中,这里应该调用您的后端API获取STS凭证
|
||
// 这里只是示例数据
|
||
return STSCredentials(
|
||
accessKeyId: 'STS.your_sts_access_key_id',
|
||
accessKeySecret: 'your_sts_access_key_secret',
|
||
securityToken: 'your_security_token',
|
||
expiration: DateTime.now().add(const Duration(hours: 1)),
|
||
);
|
||
}
|
||
|
||
// 创建STS认证提供者
|
||
final stsCredentials = await getSTSCredentials();
|
||
final authProvider = STSAuthProvider(
|
||
accessKeyId: stsCredentials.accessKeyId,
|
||
accessKeySecret: stsCredentials.accessKeySecret,
|
||
securityToken: stsCredentials.securityToken,
|
||
expiration: stsCredentials.expiration,
|
||
credentialsGetter: getSTSCredentials, // 凭证过期时自动刷新
|
||
);
|
||
|
||
const configProvider = StaticConfigProvider(
|
||
endpoint: 'oss-cn-hangzhou.aliyuncs.com',
|
||
bucketName: 'your-bucket-name',
|
||
directory: 'sts-uploads',
|
||
);
|
||
|
||
final config = YxOSSConfig(
|
||
authProvider: authProvider,
|
||
configProvider: configProvider,
|
||
);
|
||
|
||
final client = YxOSSClient(config);
|
||
|
||
try {
|
||
await client.initialize();
|
||
|
||
final fileData = Uint8List.fromList('STS upload test'.codeUnits);
|
||
|
||
final result = await client.uploadBytes(
|
||
fileData,
|
||
'sts-test-file.txt',
|
||
options: UploadOptions(
|
||
callbacks: UploadCallbacks(
|
||
onProgress: (sent, total) {
|
||
print('STS上传进度: ${(sent / total * 100).toStringAsFixed(1)}%');
|
||
},
|
||
),
|
||
),
|
||
);
|
||
|
||
print('STS上传成功: ${result.url}');
|
||
} catch (e) {
|
||
print('STS上传失败: $e');
|
||
} finally {
|
||
client.dispose();
|
||
}
|
||
}
|
||
|
||
/// 示例3: 预签名URL认证
|
||
/// 适用场景: 客户端直接上传,服务端控制权限
|
||
Future<void> example3PreSignedAuth() async {
|
||
print('\n=== 示例3: 预签名URL认证 ===');
|
||
|
||
// 模拟从服务器获取预签名URL的函数
|
||
Future<String> getPreSignedUrl(String fileName) async {
|
||
// 实际应用中,这里应该调用您的后端API获取预签名URL
|
||
// 后端会根据文件名和权限策略生成预签名URL
|
||
return 'https://your-bucket.oss-cn-hangzhou.aliyuncs.com/uploads/$fileName?Expires=1234567890&OSSAccessKeyId=your_key&Signature=signature';
|
||
}
|
||
|
||
// 创建预签名URL认证提供者
|
||
final authProvider = PreSignedAuthProvider(
|
||
urlGetter: getPreSignedUrl,
|
||
);
|
||
|
||
// 注意:预签名URL模式下,配置提供者仍然需要,但某些配置可能不会被使用
|
||
const configProvider = StaticConfigProvider(
|
||
endpoint: 'oss-cn-hangzhou.aliyuncs.com',
|
||
bucketName: 'your-bucket-name',
|
||
directory: '', // 预签名URL通常已包含完整路径
|
||
);
|
||
|
||
final config = YxOSSConfig(
|
||
authProvider: authProvider,
|
||
configProvider: configProvider,
|
||
);
|
||
|
||
final client = YxOSSClient(config);
|
||
|
||
try {
|
||
await client.initialize();
|
||
|
||
final fileData = Uint8List.fromList('PreSigned URL upload test'.codeUnits);
|
||
|
||
final result = await client.uploadBytes(
|
||
fileData,
|
||
'presigned-test-file.txt',
|
||
options: UploadOptions(
|
||
callbacks: UploadCallbacks(
|
||
onProgress: (sent, total) {
|
||
print('预签名上传进度: ${(sent / total * 100).toStringAsFixed(1)}%');
|
||
},
|
||
),
|
||
),
|
||
);
|
||
|
||
print('预签名上传成功: ${result.url}');
|
||
} catch (e) {
|
||
print('预签名上传失败: $e');
|
||
} finally {
|
||
client.dispose();
|
||
}
|
||
}
|
||
|
||
/// 示例4: 动态配置
|
||
/// 适用场景: 配置信息需要从服务器动态获取
|
||
Future<void> example4DynamicConfig() async {
|
||
print('\n=== 示例4: 动态配置 ===');
|
||
|
||
// 模拟从服务器获取OSS配置的函数
|
||
Future<OSSConfig> getOSSConfig() async {
|
||
// 实际应用中,这里应该调用您的后端API获取OSS配置
|
||
return const OSSConfig(
|
||
endpoint: 'oss-cn-hangzhou.aliyuncs.com',
|
||
bucketName: 'dynamic-bucket-name',
|
||
directory: 'dynamic-uploads',
|
||
domain: 'https://cdn.example.com',
|
||
);
|
||
}
|
||
|
||
const authProvider = StaticAuthProvider(
|
||
accessKeyId: 'your_access_key_id',
|
||
accessKeySecret: 'your_access_key_secret',
|
||
);
|
||
|
||
// 创建动态配置提供者
|
||
final configProvider = DynamicConfigProvider(
|
||
configGetter: getOSSConfig,
|
||
cacheTimeout: const Duration(minutes: 30), // 配置缓存30分钟
|
||
);
|
||
|
||
final config = YxOSSConfig(
|
||
authProvider: authProvider,
|
||
configProvider: configProvider,
|
||
);
|
||
|
||
final client = YxOSSClient(config);
|
||
|
||
try {
|
||
await client.initialize();
|
||
|
||
final fileData = Uint8List.fromList('Dynamic config upload test'.codeUnits);
|
||
|
||
final result = await client.uploadBytes(
|
||
fileData,
|
||
'dynamic-config-test.txt',
|
||
options: UploadOptions(
|
||
callbacks: UploadCallbacks(
|
||
onProgress: (sent, total) {
|
||
print('动态配置上传进度: ${(sent / total * 100).toStringAsFixed(1)}%');
|
||
},
|
||
),
|
||
),
|
||
);
|
||
|
||
print('动态配置上传成功: ${result.url}');
|
||
} catch (e) {
|
||
print('动态配置上传失败: $e');
|
||
} finally {
|
||
client.dispose();
|
||
}
|
||
}
|
||
|
||
/// 示例5: 分片上传
|
||
/// 适用场景: 大文件上传,需要支持断点续传和并发上传
|
||
Future<void> example5MultipartUpload() async {
|
||
print('\n=== 示例5: 分片上传 ===');
|
||
|
||
const authProvider = StaticAuthProvider(
|
||
accessKeyId: 'your_access_key_id',
|
||
accessKeySecret: 'your_access_key_secret',
|
||
);
|
||
|
||
const configProvider = StaticConfigProvider(
|
||
endpoint: 'oss-cn-hangzhou.aliyuncs.com',
|
||
bucketName: 'your-bucket-name',
|
||
directory: 'large-files',
|
||
);
|
||
|
||
const config = YxOSSConfig(
|
||
authProvider: authProvider,
|
||
configProvider: configProvider,
|
||
);
|
||
|
||
final client = YxOSSClient(config);
|
||
|
||
try {
|
||
await client.initialize();
|
||
|
||
// 模拟大文件数据(200MB)
|
||
final largeFileData = Uint8List(200 * 1024 * 1024);
|
||
for (int i = 0; i < largeFileData.length; i++) {
|
||
largeFileData[i] = i % 256;
|
||
}
|
||
|
||
print(
|
||
'准备上传 ${YxOSSFileUtils.formatFileSize(largeFileData.length)} 的大文件...');
|
||
|
||
final options = UploadOptions(
|
||
// 启用分片上传
|
||
enableMultipart: true,
|
||
// 分片大小设置为10MB
|
||
partSize: 10 * 1024 * 1024,
|
||
// 并发上传3个分片
|
||
concurrency: 3,
|
||
// 100MB以上的文件使用分片上传
|
||
multipartThreshold: 100 * 1024 * 1024,
|
||
callbacks: UploadCallbacks(
|
||
onStart: () => print('开始分片上传...'),
|
||
onProgress: (sent, total) {
|
||
final progress = (sent / total * 100).toStringAsFixed(1);
|
||
final speed = YxOSSFileUtils.formatFileSize(sent ~/ 10); // 假设10秒速度
|
||
print('分片上传进度: $progress% - 速度: $speed');
|
||
},
|
||
onSuccess: (result) {
|
||
if (result is MultipartUploadResult) {
|
||
print('分片上传成功:');
|
||
print(' URL: ${result.url}');
|
||
print(' 分片数量: ${result.partCount}');
|
||
print(' 上传ID: ${result.uploadId}');
|
||
print(' 总耗时: ${result.uploadDuration} 毫秒');
|
||
}
|
||
},
|
||
onError: (error) => print('分片上传失败: $error'),
|
||
onComplete: () => print('分片上传完成'),
|
||
),
|
||
);
|
||
|
||
final result = await client.uploadBytes(
|
||
largeFileData,
|
||
'large-file-${DateTime.now().millisecondsSinceEpoch}.dat',
|
||
options: options,
|
||
);
|
||
|
||
print('大文件上传结果: ${result.url}');
|
||
} catch (e) {
|
||
print('大文件上传失败: $e');
|
||
} finally {
|
||
client.dispose();
|
||
}
|
||
}
|
||
|
||
/// 示例6: 错误处理和重试
|
||
Future<void> example6ErrorHandling() async {
|
||
print('\n=== 示例6: 错误处理和重试 ===');
|
||
|
||
const authProvider = StaticAuthProvider(
|
||
accessKeyId: 'invalid_key', // 故意使用错误的key来演示错误处理
|
||
accessKeySecret: 'invalid_secret',
|
||
);
|
||
|
||
const configProvider = StaticConfigProvider(
|
||
endpoint: 'oss-cn-hangzhou.aliyuncs.com',
|
||
bucketName: 'your-bucket-name',
|
||
);
|
||
|
||
const config = YxOSSConfig(
|
||
authProvider: authProvider,
|
||
configProvider: configProvider,
|
||
// 配置重试策略
|
||
retryConfig: RetryConfig(
|
||
maxRetries: 3,
|
||
initialInterval: 1000,
|
||
backoffMultiplier: 2.0,
|
||
enableExponentialBackoff: true,
|
||
),
|
||
);
|
||
|
||
final client = YxOSSClient(config);
|
||
|
||
try {
|
||
await client.initialize();
|
||
|
||
final fileData = Uint8List.fromList('Error handling test'.codeUnits);
|
||
|
||
await client.uploadBytes(
|
||
fileData,
|
||
'error-test.txt',
|
||
options: UploadOptions(
|
||
callbacks: UploadCallbacks(
|
||
onError: (error) {
|
||
print('捕获到错误:');
|
||
print(' 类型: ${error.type}');
|
||
print(' 消息: ${error.message}');
|
||
print(' 状态码: ${error.statusCode ?? 'N/A'}');
|
||
print(' 请求ID: ${error.requestId ?? 'N/A'}');
|
||
print(' 是否可重试: ${error.isRetryable}');
|
||
},
|
||
),
|
||
),
|
||
);
|
||
} catch (e) {
|
||
if (e is OSSError) {
|
||
print('OSS错误详情:');
|
||
print(' 错误类型: ${e.type}');
|
||
print(' 错误代码: ${e.code}');
|
||
print(' 错误消息: ${e.message}');
|
||
print(' HTTP状态码: ${e.statusCode}');
|
||
print(' 是否为认证错误: ${e.isAuthenticationError}');
|
||
print(' 是否为网络错误: ${e.isNetworkError}');
|
||
} else {
|
||
print('未知错误: $e');
|
||
}
|
||
} finally {
|
||
client.dispose();
|
||
}
|
||
}
|
||
|
||
/// 示例7: 集成到项目中的适配器模式
|
||
class ProjectOSSAdapter {
|
||
late final YxOSSClient _client;
|
||
|
||
/// 初始化适配器
|
||
Future<void> initialize({
|
||
required String endpoint,
|
||
required String bucketName,
|
||
required String accessKeyId,
|
||
required String accessKeySecret,
|
||
String? customDomain,
|
||
}) async {
|
||
final authProvider = StaticAuthProvider(
|
||
accessKeyId: accessKeyId,
|
||
accessKeySecret: accessKeySecret,
|
||
);
|
||
|
||
final configProvider = StaticConfigProvider(
|
||
endpoint: endpoint,
|
||
bucketName: bucketName,
|
||
directory: 'app-uploads',
|
||
domain: customDomain,
|
||
);
|
||
|
||
final config = YxOSSConfig(
|
||
authProvider: authProvider,
|
||
configProvider: configProvider,
|
||
logConfig: const LogConfig(
|
||
enabled: true,
|
||
level: LogLevel.warning, // 生产环境只记录警告和错误
|
||
),
|
||
);
|
||
|
||
_client = YxOSSClient(config);
|
||
await _client.initialize();
|
||
}
|
||
|
||
/// 上传图片文件
|
||
Future<String> uploadImage(
|
||
Uint8List imageData,
|
||
String fileName, {
|
||
Function(double progress)? onProgress,
|
||
}) async {
|
||
final result = await _client.uploadBytes(
|
||
imageData,
|
||
'images/${YxOSSFileUtils.generateUniqueFileName(originalFileName: fileName)}',
|
||
options: UploadOptions(
|
||
contentType: YxOSSFileUtils.guessMimeType(fileName),
|
||
acl: ACLMode.publicRead,
|
||
callbacks: UploadCallbacks(
|
||
onProgress: (sent, total) {
|
||
onProgress?.call(sent / total);
|
||
},
|
||
),
|
||
),
|
||
);
|
||
|
||
return result.url;
|
||
}
|
||
|
||
/// 上传文档文件
|
||
Future<String> uploadDocument(
|
||
Uint8List documentData,
|
||
String fileName, {
|
||
Function(double progress)? onProgress,
|
||
}) async {
|
||
final result = await _client.uploadBytes(
|
||
documentData,
|
||
'documents/${YxOSSFileUtils.generateUniqueFileName(originalFileName: fileName)}',
|
||
options: UploadOptions(
|
||
contentType: YxOSSFileUtils.guessMimeType(fileName),
|
||
acl: ACLMode.private, // 文档默认私有
|
||
callbacks: UploadCallbacks(
|
||
onProgress: (sent, total) {
|
||
onProgress?.call(sent / total);
|
||
},
|
||
),
|
||
),
|
||
);
|
||
|
||
return result.url;
|
||
}
|
||
|
||
/// 释放资源
|
||
void dispose() {
|
||
_client.dispose();
|
||
}
|
||
}
|