You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter应用中实现视频直接断点续传至Azure Blob

Flutter实现视频直接断点续传至Azure Blob

要绕开tus服务器直接实现断点续传,核心是利用Azure Blob的块Blob分块上传机制——将视频拆分为多个小块上传,记录已上传的块,中断后跳过已上传部分继续传输,最后提交块列表完成完整Blob的创建。

实现步骤

1. 添加依赖

在pubspec.yaml中引入Azure Blob存储及文件操作相关依赖:

dependencies:
  azure_storage_blobs: ^2.0.0 # 适配最新版本即可
  path_provider: ^2.0.0
  flutter:
    sdk: flutter

2. 初始化Azure Blob客户端

通过连接字符串或SAS令牌初始化Blob客户端(生产环境建议由后端生成临时SAS令牌,避免客户端暴露敏感信息):

import 'package:azure_storage_blobs/azure_storage_blobs.dart';

// 示例:使用连接字符串初始化(仅用于测试,生产用SAS令牌)
final blobServiceClient = BlobServiceClient.fromConnectionString(
  "你的Azure存储连接字符串",
);
final containerClient = blobServiceClient.getBlobContainerClient("目标容器名");

3. 核心断点续传逻辑

3.1 获取已上传的块列表

上传前先查询目标Blob是否存在未完成的块,避免重复上传:

Future<List<String>> getUploadedBlockIds(String blobName) async {
  try {
    final blobClient = containerClient.getBlobClient(blobName);
    final blockList = await blobClient.getBlockList(blockListType: BlockListType.all);
    return blockList.uncommittedBlocks.map((b) => b.name).toList();
  } catch (_) {
    // Blob不存在或无未完成块,返回空列表
    return [];
  }
}

3.2 分块读取本地视频

按指定大小拆分本地视频文件,生成数据流:

import 'dart:io';

Stream<List<int>> readVideoChunks(File videoFile, int chunkSize) async* {
  final totalSize = await videoFile.length();
  int offset = 0;
  while (offset < totalSize) {
    final end = offset + chunkSize > totalSize ? totalSize : offset + chunkSize;
    final chunk = await videoFile.openRead(offset, end).first;
    yield chunk;
    offset = end;
  }
}

3.3 断点续传上传

遍历视频分块,跳过已上传的块,上传剩余部分,最后提交块列表完成上传:

import 'dart:convert';

Future<void> uploadVideoWithResume(
  File videoFile,
  String targetBlobName, {
  int chunkSize = 10 * 1024 * 1024, // 10MB每块,可根据需求调整
}) async {
  final uploadedBlockIds = await getUploadedBlockIds(targetBlobName);
  final totalSize = await videoFile.length();
  int currentOffset = 0;
  int chunkIndex = 0;
  final allBlockIds = <String>[];

  await for (final chunk in readVideoChunks(videoFile, chunkSize)) {
    // 生成唯一且稳定的块ID(确保中断后能匹配已上传块)
    final blockId = base64.encode(utf8.encode('block-$chunkIndex'));
    
    if (uploadedBlockIds.contains(blockId)) {
      // 跳过已上传的块
      currentOffset += chunk.length;
      allBlockIds.add(blockId);
      chunkIndex++;
      print('跳过已上传块 $chunkIndex,进度:${(currentOffset/totalSize*100).toStringAsFixed(2)}%');
      continue;
    }

    // 上传当前块
    final blobClient = containerClient.getBlobClient(targetBlobName);
    await blobClient.stageBlock(
      blockId: blockId,
      blockData: Stream.value(chunk),
      length: chunk.length,
    );
    
    allBlockIds.add(blockId);
    currentOffset += chunk.length;
    chunkIndex++;
    print('上传块 $chunkIndex,进度:${(currentOffset/totalSize*100).toStringAsFixed(2)}%');
  }

  // 提交所有块,完成完整Blob的创建
  final blobClient = containerClient.getBlobClient(targetBlobName);
  await blobClient.commitBlockList(allBlockIds);
  print('视频上传完成');
}

4. 关键注意事项

  • 块ID稳定性:必须使用固定规则生成块ID,否则中断后无法匹配已上传块。
  • 权限安全:生产环境不要在客户端硬编码连接字符串,应由后端生成带过期时间的SAS令牌供客户端使用。
  • 块数量限制:Azure块Blob最多支持50000个块,需根据视频大小调整分块大小(如10MB分块最大支持500GB视频)。
  • 未提交块清理:未提交的块会在7天后被Azure自动清理,若长时间未续传需重新上传。

内容的提问来源于stack exchange,提问作者Gowtham M

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 05:10:08