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
相关产品推荐
相关产品推荐

