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

优化Firebase Storage文件上传:解决卡顿与崩溃问题

Firebase Storage 文件上传优化方案求助

我用下面的代码向Firebase Storage上传文件,原代码冗余且运行缓慢,修改后的代码在第2或3次上传时会导致应用崩溃,求可靠高效的优化方案。

原上传代码

const uploadToStorage = async (uri: string, path: string, category: string, subcategory: string): Promise<string> => {
    try {
        const filename = `${Date.now()}-${Math.random().toString(36).substring(7)}.jpg`; // Unique filename
        const storageRef = ref(storage, `/${category}/${subcategory}/${filename}`);

        const response = await fetch(uri);
        const blob = await response.blob();

        const uploadTask = uploadBytesResumable(storageRef, blob);

        return new Promise<string>((resolve, reject) => {
            uploadTask.on('state_changed',
                (snapshot) => {
                    // Optional: Update progress to the user
                    const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
                    console.log(`Upload is ${progress}% done`);
                },
                reject, // Handle unsuccessful uploads
                async () => {
                    try {
                        const downloadURL = await getDownloadURL(uploadTask.snapshot.ref);
                        resolve(downloadURL); // Resolve with the download URL
                    } catch (error) {
                        console.error(`Error getting download URL:`, error);
                        reject(error);
                    }
                }
            );
        });
    } catch (error) {
        console.error("Error during the upload process", error);
        throw error;
    }
};

修改后易崩溃的代码

const uploadToStorage = async (uri: string, path: string, category: string, subcategory: string): Promise<string> => {
    try {
        const filename = `${Date.now()}-${Math.random().toString(36).substring(7)}.jpg`; // Unique filename
        const storageRef = ref(storage, `/${category}/${subcategory}/${filename}`);

        const response = await fetch(uri);
        const blob = await response.blob();

        const uploadTask = uploadBytes(storageRef, blob);

        return new Promise<string>((resolve, reject) => {
            uploadTask.then(
                async (snapshot) => {
                    const downloadURL = await getDownloadURL(snapshot.ref);
                    resolve(downloadURL);
                },
            ).catch((error) => {
                console.error("Error uploading image", error);
                reject(error);
            });
        });
    } catch (error) {
        console.error("Error during the upload process", error);
        throw error;
    }
};

崩溃原因分析

  1. 冗余Promise嵌套:uploadBytes本身返回Promise,外层额外套new Promise导致Promise链混乱,容易出现未处理的异步错误或内存泄漏
  2. 资源未释放:多次上传后Blob对象未被清理,内存占用持续升高,最终触发应用崩溃
  3. 错误处理不完整:部分异常场景未被捕获,导致未处理的Promise rejection引发崩溃

优化后的可靠高效代码

import { ref, uploadBytesResumable, getDownloadURL } from "firebase/storage";

const uploadToStorage = async (uri: string, category: string, subcategory: string): Promise<string> => {
  // 用crypto.randomUUID生成更可靠的唯一文件名,避免时间戳重复
  const filename = `${crypto.randomUUID()}.jpg`;
  const storageRef = ref(storage, `/${category}/${subcategory}/${filename}`);

  let response: Response | null = null;
  let blob: Blob | null = null;

  try {
    response = await fetch(uri);
    // 提前检查请求是否成功,避免无效资源流入后续流程
    if (!response.ok) throw new Error(`Failed to fetch file: ${response.statusText}`);
    
    blob = await response.blob();
    
    // 保留uploadBytesResumable支持断点续传,网络波动时稳定性更强
    const uploadTask = uploadBytesResumable(storageRef, blob);
    
    // 用Promise封装状态监听,避免嵌套回调
    const snapshot = await new Promise<import("firebase/storage").UploadTaskSnapshot>((resolve, reject) => {
      uploadTask.on('state_changed',
        (snap) => {
          // 进度回调可按需抽成参数传入,方便外部更新UI
          const progress = (snap.bytesTransferred / snap.totalBytes) * 100;
          console.log(`Upload progress: ${progress}%`);
        },
        reject,
        () => resolve(uploadTask.snapshot)
      );
    });

    const downloadURL = await getDownloadURL(snapshot.ref);
    return downloadURL;
  } catch (error) {
    console.error("Upload failed:", error);
    throw error;
  } finally {
    // 清理资源,避免内存泄漏
    if (blob) blob = null;
    response?.body?.cancel(); // 取消未完成的请求释放连接
  }
};

优化说明

  • 简化Promise逻辑:去掉不必要的嵌套,用async/await统一处理异步流程,代码更清晰易维护
  • 可靠的唯一ID:crypto.randomUUID()比时间戳+随机字符串更安全,不会出现重复文件名
  • 资源自动清理:在finally块中释放Blob和响应资源,避免多次上传后内存溢出
  • 增强错误检查:提前验证fetch响应状态,更早发现无效文件问题
  • 稳定的上传方式:保留uploadBytesResumable,支持断点续传,大文件或不稳定网络下表现更优
  • 可扩展性:进度回调可抽成参数,方便外部对接UI进度条

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 06:43:23