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

如何优化图片与视频同步上传至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();
}

关键优化点说明

  1. 并行处理:通过Future.wait同时启动所有资产的压缩、上传任务,避免串行等待的低效问题,大幅提升整体上传速度。
  2. 进度追踪:通过onProgress回调实时返回总上传进度,可直接绑定UI展示进度条;单个文件的实时进度可在_uploadToStorage中通过uploadTask.snapshotEvents监听。
  3. 压缩优化:针对图片和视频分别使用专业压缩库,在保证画质的前提下减小文件体积,进一步加速上传。
  4. 原子性写入:保留Firestore的WriteBatch,确保所有Asset文档要么全部写入成功,要么全部失败,保证数据一致性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 00:50:04