优化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; } };
崩溃原因分析
- 冗余Promise嵌套:
uploadBytes本身返回Promise,外层额外套new Promise导致Promise链混乱,容易出现未处理的异步错误或内存泄漏 - 资源未释放:多次上传后Blob对象未被清理,内存占用持续升高,最终触发应用崩溃
- 错误处理不完整:部分异常场景未被捕获,导致未处理的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
相关产品推荐
相关产品推荐

