dart_aliyun_oss 1.0.3 copy "dart_aliyun_oss: ^1.0.3" to clipboard
dart_aliyun_oss: ^1.0.3 copied to clipboard

Dart client SDK for Alibaba Cloud Object Storage Service (OSS). Supports file upload, download, multipart upload, signed URLs and more.

example/example.dart

// ignore_for_file: avoid_print
import 'dart:async'; // 导入 async 包
import 'dart:io';

import 'package:dart_aliyun_oss/dart_aliyun_oss.dart';
import 'package:dio/dio.dart';

import 'config.dart'; // 导入配置文件

/// 将 OSSClient 初始化移到全局或 main 函数顶部,以便所有示例函数都能访问
late final OSSClient oss;

/// 全局签名版本设置,默认使用 V1 签名
bool isV1Signature = true;

/// 获取当前签名版本名称
String get signatureVersionName => isV1Signature ? 'V1' : 'V4';

/// 选择签名版本
void selectSignatureVersion() {
  print('\n当前签名版本: $signatureVersionName');
  stdout.write('请选择签名版本 (1: V1签名, 4: V4签名, 回车保持当前设置): ');
  final String? versionChoice = stdin.readLineSync();

  if (versionChoice == '4') {
    isV1Signature = false;
    print('已切换到 V4 签名版本');
  } else if (versionChoice == '1') {
    isV1Signature = true;
    print('已切换到 V1 签名版本');
  } else {
    print('保持当前签名版本: $signatureVersionName');
  }
}

/// 示例 1: 简单上传
Future<void> _runSimpleUploadExample() async {
  print('\n--- 运行示例 1: 简单上传 ---');
  try {
    final File file = File('example/assets/example.txt');
    if (!file.existsSync()) {
      print('错误: 文件 ${file.path} 不存在');
      return;
    }

    await oss.putObject(
      file,
      'example/test_oss_put.txt',
      params: OSSRequestParams(
        isV1Signature: isV1Signature, // 使用全局签名版本设置
        onSendProgress: (int count, int total) {
          // 处理上传进度,用百分比展示
          if (total > 0) {
            print('上传进度: ${(count / total * 100).toStringAsFixed(2)}%');
          } else {
            print('上传进度: $count bytes');
          }
        },
      ),
    );
    print('文件上传成功');
  } catch (e) {
    print('文件上传失败: $e');
  }
  print('--- 示例 1 结束 ---\n');
}

/// 示例 2: 下载文件
Future<void> _runDownloadExample() async {
  print('\n--- 运行示例 2: 下载文件 ---');
  try {
    const String ossObjectKey = 'example/test_oss_put.txt'; // 要下载的文件
    const String downloadPath = 'example/downloaded/example.txt'; // 保存路径

    final Response<dynamic> response = await oss.getObject(
      ossObjectKey,
      params: OSSRequestParams(
        isV1Signature: isV1Signature, // 使用全局签名版本设置
        onReceiveProgress: (int count, int total) {
          // 避免除以零
          if (total > 0) {
            print('下载进度: ${(count / total * 100).toStringAsFixed(2)}%');
          } else {
            print('下载进度: $count bytes (总大小未知)');
          }
        },
      ),
    );

    final File downloadFile = File(downloadPath);
    // 确保目录存在
    await downloadFile.parent.create(recursive: true);
    await downloadFile.writeAsBytes(response.data);

    print('文件下载成功,保存路径: $downloadPath');
  } catch (e) {
    print('文件下载失败: $e');
  }
  print('--- 示例 2 结束 ---\n');
}

/// 示例 3: 分片上传文件 (使用封装后的方法)
Future<void> _runMultipartUploadExample() async {
  print('\n--- 运行示例 3: 分片上传 (使用封装方法) ---');
  const String localFilePath = 'example/assets/large_file.bin'; // 本地文件路径
  const String ossObjectKey =
      'example/multipart_upload_example.bin'; // 上传到 OSS 的路径

  // 记录开始时间
  final DateTime startTime = DateTime.now();
  print('开始时间: $startTime');

  try {
    final File file = File(localFilePath);
    if (!file.existsSync()) {
      print('错误: 文件 $localFilePath 不存在');
      return;
    }

    print('开始分片上传 (封装方法): $localFilePath -> $ossObjectKey');

    // --- 调用封装后的 multipartUpload 方法 ---
    final Response<CompleteMultipartUploadResult> completeResponse =
        await oss.multipartUpload(
      file,
      ossObjectKey,
      // numberOfParts: 5, // 可选:传入期望的分片数
      params: OSSRequestParams(
        isV1Signature: isV1Signature, // 使用全局签名版本设置
        onSendProgress: (int count, int total) {
          if (total > 0) {
            print('  整体上传进度: ${(count / total * 100).toStringAsFixed(2)}%');
          } else {
            print('  整体上传进度: $count bytes');
          }
        },
      ),
      onPartProgress: (int partNumber, int count, int total) {
        if (total > 0) {
          // 可以选择性地打印分片进度,避免过多日志
          // print('    分片 $partNumber 上传进度: ${(count / total * 100).toStringAsFixed(2)}%');
        }
      },
      // cancelToken: myCancelToken, // 可选:传入 CancelToken
    );

    print('分片上传成功完成!');
    print('  OSS Location: ${completeResponse.data?.location}');
    print('  OSS Bucket: ${completeResponse.data?.bucket}');
    print('  OSS Key: ${completeResponse.data?.key}');
    print('  OSS ETag: ${completeResponse.data?.eTag}');

    // 获取并打印实际使用的分片数量
    final String? actualPartsCount = completeResponse.data?.eTag
        .split('-')
        .lastOrNull; // eTag 格式通常为 "xxx-N",其中 N 为分片数量
    if (actualPartsCount != null) {
      print('  实际分片数量: $actualPartsCount');
    }
  } catch (e) {
    // multipartUpload 方法内部已处理 abort,这里只需捕获最终错误
    print('分片上传失败: $e');
    if (e is OSSException) {
      print('  错误类型: ${e.type}');
      print('  原始响应: ${e.response}');
    }
  } finally {
    // 记录结束时间并计算耗时
    final DateTime endTime = DateTime.now();
    final Duration duration = endTime.difference(startTime);
    print('结束时间: $endTime');
    print('总耗时: ${duration.inSeconds} 秒 (${duration.inMilliseconds} 毫秒)');
  }
  print('--- 示例 3 结束 ---\n');
}

/// 示例 4: 列出已上传的分片 (手动输入 Object Key 和 Upload ID)
Future<void> _runListPartsExample() async {
  print('\n--- 运行示例 4: 列出已上传的分片 ---');

  // --- 从终端获取输入 ---
  String? ossObjectKey;
  while (ossObjectKey == null || ossObjectKey.isEmpty) {
    stdout.write(
      '请输入要查询的 Object Key (例如: example/multipart_upload_example.bin): ',
    );
    ossObjectKey = stdin.readLineSync();
    if (ossObjectKey == null || ossObjectKey.isEmpty) {
      print('错误: Object Key 不能为空。');
    }
  }

  String? uploadId;
  while (uploadId == null || uploadId.isEmpty) {
    stdout.write('请输入要查询的 Upload ID: ');
    uploadId = stdin.readLineSync();
    if (uploadId == null || uploadId.isEmpty) {
      print('错误: Upload ID 不能为空。');
    }
  }
  // --- 输入获取结束 ---

  print('\n尝试列出对象 \'$ossObjectKey\' (Upload ID: $uploadId) 的已上传分片...');

  try {
    // 可以添加分页参数 maxParts, partNumberMarker
    final Response<ListPartsResult> response = await oss.listParts(
      ossObjectKey, // 使用用户输入的 Key
      uploadId, // 使用用户输入的 Upload ID
      // maxParts: 10, // 可选:限制返回的分片数量
      // partNumberMarker: 5, // 可选:从指定分片号之后开始列出
      params: OSSRequestParams(
        isV1Signature: isV1Signature, // 使用全局签名版本设置
      ),
    );

    final ListPartsResult? result = response.data;
    if (result != null) {
      print('列出分片成功:');
      print('  Bucket: ${result.bucket}');
      print('  Key: ${result.key}');
      print('  Upload ID: ${result.uploadId}');
      print('  Next Part Number Marker: ${result.nextPartNumberMarker}');
      print('  Max Parts: ${result.maxParts}');
      print('  Is Truncated: ${result.isTruncated}');
      print('  Encoding Type: ${result.encodingType}');
      print('  Parts (${result.parts.length} 个):');
      for (final PartInfo part in result.parts) {
        print(
          '    - PartNumber: ${part.partNumber}, ETag: ${part.eTag}, Size: ${part.size}, LastModified: ${part.lastModified}',
        );
      }
    } else {
      print('列出分片失败,未收到有效数据。状态码: ${response.statusCode}');
    }
  } catch (e) {
    print('列出分片时出错: $e');
  }
  print('--- 示例 4 结束 ---\n');
}

/// 示例 5: 列出所有进行中的分片上传事件
Future<void> _runListMultipartUploadsExample() async {
  print('\n--- 运行示例 5: 列出所有进行中的分片上传事件 ---');
  try {
    print('尝试列出存储桶中所有进行中的分片上传...');
    // 可以添加过滤和分页参数: prefix, delimiter, keyMarker, uploadIdMarker, maxUploads
    final Response<ListMultipartUploadsResult> response =
        await oss.listMultipartUploads(
      // prefix: 'example/', // 可选:只列出指定前缀的
      // maxUploads: 5, // 可选:限制返回数量
      params: OSSRequestParams(
        isV1Signature: isV1Signature, // 使用全局签名版本设置
      ),
    );

    final ListMultipartUploadsResult? result = response.data;
    if (result != null) {
      print('列出进行中的分片上传成功:');
      print('  Bucket: ${result.bucket}');
      print('  Prefix: ${result.prefix}');
      print('  Delimiter: ${result.delimiter}');
      print('  Key Marker: ${result.keyMarker}');
      print('  Upload ID Marker: ${result.uploadIdMarker}');
      print('  Next Key Marker: ${result.nextKeyMarker}');
      print('  Next Upload ID Marker: ${result.nextUploadIdMarker}');
      print('  Max Uploads: ${result.maxUploads}');
      print('  Is Truncated: ${result.isTruncated}');
      print('  Encoding Type: ${result.encodingType}');
      print('  Uploads (${result.uploads.length} 个):');
      for (final UploadInfo upload in result.uploads) {
        print(
          '    - Key: ${upload.key}, Upload ID: ${upload.uploadId}, Initiated: ${upload.initiated}',
        );
      }
      print('  Common Prefixes (${result.commonPrefixes.length} 个):');
      for (final String prefix in result.commonPrefixes) {
        print('    - $prefix');
      }
    } else {
      print('列出进行中的分片上传失败,未收到有效数据。状态码: ${response.statusCode}');
    }
  } catch (e) {
    print('列出进行中的分片上传时出错: $e');
  }
  print('--- 示例 5 结束 ---\n');
}

/// 示例 6: 终止分片上传 (手动输入 Object Key 和 Upload ID)
Future<void> _runAbortMultipartUploadExample() async {
  print('\n--- 运行示例 6: 终止分片上传 ---');

  // --- 从终端获取输入 ---
  String? ossObjectKey;
  while (ossObjectKey == null || ossObjectKey.isEmpty) {
    stdout.write('请输入要终止上传的 Object Key: ');
    ossObjectKey = stdin.readLineSync();
    if (ossObjectKey == null || ossObjectKey.isEmpty) {
      print('错误: Object Key 不能为空。');
    }
  }

  String? uploadId;
  while (uploadId == null || uploadId.isEmpty) {
    stdout.write('请输入要终止上传的 Upload ID: ');
    uploadId = stdin.readLineSync();
    if (uploadId == null || uploadId.isEmpty) {
      print('错误: Upload ID 不能为空。');
    }
  }
  // --- 输入获取结束 ---

  try {
    // 直接尝试终止用户指定的分片上传
    print('\n尝试终止对象 \'$ossObjectKey\' 的分片上传 (Upload ID: $uploadId)...');
    await oss.abortMultipartUpload(
      ossObjectKey,
      uploadId,
      params: OSSRequestParams(
        isV1Signature: isV1Signature, // 使用全局签名版本设置
      ),
    );
    print('分片上传 (Upload ID: $uploadId) 已成功终止。');
  } catch (e) {
    print('终止分片上传过程中出错: $e');
  }
  print('--- 示例 6 结束 ---\n');
}

/// 示例 7: 生成签名 URL
///
/// 使用 OSSClient.signedUrl 方法生成签名 URL,根据全局设置使用 V1 或 V4 签名算法。
Future<void> _runGenerateSignedUrlExample() async {
  print('\n--- 运行示例 7: 生成$signatureVersionName签名 URL ---');

  // 设置签名参数
  const String ossObjectKey = 'example/test_oss_put.txt'; // 要访问的对象

  // 让用户选择过期时间
  stdout.write('请输入 URL 有效期(秒,默认 3600): ');
  final String? expiresInput = stdin.readLineSync();
  final int expires = int.tryParse(expiresInput ?? '') ?? 3600;

  // 让用户选择 HTTP 方法
  stdout.write('请选择 HTTP 方法 (1: GET, 2: PUT, 3: POST, 4: DELETE, 默认: GET): ');
  final String? methodChoice = stdin.readLineSync();

  String method;
  switch (methodChoice) {
    case '2':
      method = 'PUT';
      break;
    case '3':
      method = 'POST';
      break;
    case '4':
      method = 'DELETE';
      break;
    default:
      method = 'GET';
  }

  print('\n正在生成 $signatureVersionName 签名 URL...');
  print('  对象: $ossObjectKey');
  print('  HTTP 方法: $method');
  print('  有效期: $expires 秒');

  try {
    // 使用 OSSClient.signedUrl 方法生成签名 URL,使用全局签名版本设置
    final String signedUrl = oss.signedUrl(
      ossObjectKey,
      method: method,
      expires: expires,
      isV1Signature: isV1Signature, // 使用全局签名版本设置
      // headers: {'x-oss-meta-custom': 'value'}, // 可选:添加需要签名的头部
      // additionalHeaders: {'x-oss-meta-author'}, // 可选:指定需要参与签名的额外头部名称(仅 V4 签名)
    );

    print('\n生成的 $signatureVersionName 签名 URL (有效期 $expires 秒):');
    print(signedUrl);
    print('\n请尝试在浏览器中打开此 URL (如果对象存在且权限正确)');
  } catch (e) {
    print('生成 $signatureVersionName 签名 URL 失败: $e');
  }
  print('--- 示例 7 结束 ---\n');
}

/// 主函数,提供交互式菜单运行示例
Future<void> main() async {
  // --- 初始化 OSSClient ---
  // 从配置文件读取配置
  oss = OSSClient.init(
    const OSSConfig(
      accessKeyId: OssConfig.accessKeyId,
      accessKeySecret: OssConfig.accessKeySecret,
      bucketName: OssConfig.bucket,
      endpoint: OssConfig.endpoint,
      region: OssConfig.region, // V4 签名需要 region,
    ),
    // connectTimeout: Duration(seconds: 30), // 可选:连接超时时间
    // receiveTimeout: Duration(minutes: 5), // 可选:接收超时时间
  );

  print('OSS Client 初始化成功:');
  print('  Endpoint: ${oss.config.endpoint}');
  print('  Bucket: ${oss.config.bucketName}');
  print('  Region: ${oss.config.region}');
  print('------------------------------------\n');

  // 确保示例资源目录存在
  Directory('example/assets').createSync(recursive: true);
  // 创建一个用于上传的示例文件 (如果不存在)
  final File exampleFile = File('example/assets/example.txt');
  if (!exampleFile.existsSync()) {
    await exampleFile.writeAsString('这是一个用于测试 OSS 上传的示例文本文件。');
    print('创建了示例文件: ${exampleFile.path}');
  }
  // 创建一个用于分片上传的大文件 (如果不存在)
  final File largeFile = File('example/assets/large_file.bin');
  if (!largeFile.existsSync()) {
    print('正在创建用于分片上传的大文件 (约 10MB)...');
    final List<int> randomContent = List<int>.generate(
      10 * 1024 * 1024,
      (int index) => index % 256,
    );
    await largeFile.writeAsBytes(randomContent);
    print('创建了大文件: ${largeFile.path}');
  }

  // 交互式菜单
  while (true) {
    print('\n请选择要运行的示例:');
    print('  0: 切换签名版本 (当前: $signatureVersionName)');
    print('  1: 简单上传');
    print('  2: 下载文件');
    print('  3: 分片上传 (使用封装方法)');
    print('  4: 列出已上传的分片 (需手动输入)');
    print('  5: 列出所有进行中的分片上传');
    print('  6: 中止分片上传 (需手动输入)');
    print('  7: 生成签名 URL');
    print('  q: 退出');
    stdout.write('请输入选项: ');

    final String? choice = stdin.readLineSync();

    switch (choice) {
      case '0':
        selectSignatureVersion();
        break;
      case '1':
        await _runSimpleUploadExample();
        break;
      case '2':
        await _runDownloadExample();
        break;
      case '3':
        await _runMultipartUploadExample();
        break;
      case '4':
        await _runListPartsExample();
        break;
      case '5':
        await _runListMultipartUploadsExample();
        break;
      case '6':
        await _runAbortMultipartUploadExample();
        break;
      case '7':
        await _runGenerateSignedUrlExample();
        break;
      case 'q':
      case 'Q':
        print('退出程序。');
        return; // 退出 main 函数
      default:
        print('无效的选项,请重新输入。');
    }

    // 添加短暂延迟,避免连续输出导致混乱
    await Future<void>.delayed(const Duration(milliseconds: 500));
  }
}
4
likes
0
points
324
downloads

Publisher

unverified uploader

Weekly Downloads

Dart client SDK for Alibaba Cloud Object Storage Service (OSS). Supports file upload, download, multipart upload, signed URLs and more.

Repository (GitHub)
View/report issues

Topics

#storage #cloud #file #network #api

License

unknown (license)

Dependencies

convert, crypto, dio

More

Packages that depend on dart_aliyun_oss