如何优化图片与视频同步上传至Firebase Storage和Firestore的流程?
优化Firebase图片/视频同步上传流程方案
核心问题解决思路
1. 上传提速:并行处理替代串行循环
当前串行逐个处理资产的方式效率极低,改用Future.wait并行处理所有资产的压缩、上传流程,同时保留Firestore的WriteBatch批量写入,既提升上传速度,又保证数据库操作的原子性。
2. 进度追踪:分文件+总进度双向监控
为每个上传任务添加完成回调,汇总单个文件完成状态计算总上传进度;单个文件的实时上传进度可通过Firebase Storage的上传事件监听实现。
完整实现代码
依赖引入
确保pubspec.yaml中包含以下依赖:
dependencies: flutter_image_compress: ^1.1.0 video_compress: ^3.1.1 firebase_storage: ^11.0.0 cloud_firestore: ^4.0.0
核心实现代码
import 'dart:typed_data'; import 'package:flutter_image_compress/flutter_image_compress.dart'; import 'package:video_compress/video_compress.dart'; import 'package:firebase_storage/firebase_storage.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; // 定义进度回调类型,用于实时返回上传进度 typedef UploadProgressCallback = void Function(int uploaded, int total, double percentage); Future<void> syncAssetsToFirebase({ required List<AssetEntity> assets, required String bucketPath, required UploadProgressCallback onProgress, }) async { final WriteBatch batch = FirebaseFirestore.instance.batch(); final List<Future<void>> uploadFutures = []; final int totalFiles = assets.length; int completedFiles = 0; // 为每个资产创建并行处理任务 for (final asset in assets) { uploadFutures.add(_processSingleAsset( asset: asset, bucketPath: bucketPath, batch: batch, onAssetCompleted: () { completedFiles++; onProgress(completedFiles, totalFiles, completedFiles / totalFiles); }, )); } // 并行执行所有上传任务 await Future.wait(uploadFutures); // 批量提交Firestore文档写入 await batch.commit(); } Future<void> _processSingleAsset({ required AssetEntity asset, required String bucketPath, required WriteBatch batch, required VoidCallback onAssetCompleted, }) async { Uint8List? compressedData; Uint8List? compressedThumbnailData; String contentType; String filename; String downloadUrl; String thumbnailUrl = ''; try { if (asset.type == AssetType.video) { // 视频压缩 final compressedVideo = await VideoCompress.compressVideo( asset.path, quality: VideoQuality.MediumQuality, deleteOrigin: false, ); if (compressedVideo != null) { compressedData = await compressedVideo.file.readAsBytes(); contentType = 'video/mp4'; filename = '${DateTime.now().millisecondsSinceEpoch}.mp4'; // 生成并上传视频缩略图 final thumbnailFile = await VideoCompress.getThumbnail( asset.path, quality: 50, position: -1, ); if (thumbnailFile != null) { compressedThumbnailData = await thumbnailFile.readAsBytes(); thumbnailUrl = await _uploadToStorage( data: compressedThumbnailData, contentType: 'image/jpeg', path: '$bucketPath/thumbnails', filename: '${filename}_thumb.jpg', ); } } } else { // 图片压缩 compressedData = await FlutterImageCompress.compressWithFile( asset.path, minWidth: 1920, minHeight: 1080, quality: 70, ); contentType = 'image/jpeg'; filename = '${DateTime.now().millisecondsSinceEpoch}.jpg'; } // 上传主文件到Firebase Storage downloadUrl = await _uploadToStorage( data: compressedData!, contentType: contentType, path: bucketPath, filename: filename, ); // 获取资产地理位置 GeoPoint? location = await getAssetGeoPoint(asset); // 创建Asset模型并添加到批量写入队列 final docId = FirebaseFirestore.instance.collection('assets').doc().id; final assetModel = Asset( id: docId, downloadUrl: downloadUrl, thumbnailUrl: thumbnailUrl, contentType: contentType, location: location, // 补充模型其他字段 ); batch.set( FirebaseFirestore.instance.collection('assets').doc(docId), assetModel.toMap(), ); } catch (e) { // 单个资产处理失败的容错,可根据需求添加重试或日志记录 print('资产处理失败: $e'); } finally { onAssetCompleted(); } } Future<String> _uploadToStorage({ required Uint8List data, required String contentType, required String path, required String filename, }) async { final storageRef = FirebaseStorage.instance.ref().child('$path/$filename'); final uploadTask = storageRef.putData( data, SettableMetadata(contentType: contentType), ); // 如需监听单个文件上传进度,可在此添加: // uploadTask.snapshotEvents.listen((TaskSnapshot snapshot) { // double progress = snapshot.bytesTransferred / snapshot.totalBytes; // // 更新单个文件进度 // }); final snapshot = await uploadTask; return await snapshot.ref.getDownloadURL(); }
关键优化点说明
- 并行处理:通过
Future.wait同时启动所有资产的压缩、上传任务,避免串行等待的低效问题,大幅提升整体上传速度。 - 进度追踪:通过
onProgress回调实时返回总上传进度,可直接绑定UI展示进度条;单个文件的实时进度可在_uploadToStorage中通过uploadTask.snapshotEvents监听。 - 压缩优化:针对图片和视频分别使用专业压缩库,在保证画质的前提下减小文件体积,进一步加速上传。
- 原子性写入:保留Firestore的
WriteBatch,确保所有Asset文档要么全部写入成功,要么全部失败,保证数据一致性。
内容的提问来源于stack exchange,提问作者Spencer
相关产品推荐
相关产品推荐

