Expo项目使用uploadBytesResumable上传Firebase Storage图片时随机崩溃
问题
我开发了一个Expo项目,需要将用户头像上传至Firebase Storage。除图片上传外其余功能均正常,但上传时会随机崩溃,无任何错误提示。该问题在iOS和Android的模拟器及真机上均会出现,崩溃发生在上传进度18%-80%之间。
相关代码如下:
import { getStorage, ref, uploadBytesResumable, getDownloadURL } from "firebase/storage"; const uploadImage = async (theImage) => { const response = await fetch(theImage); const file = await response.blob(); const storageRef = ref(storage, `images/${signedUser.uid}`); const uploadTask = uploadBytesResumable(storageRef, file); // Listen for state changes, errors, and completion of the upload. uploadTask.on('state_changed', (snapshot) => { // Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; console.log('Upload is ' + progress + '% done'); switch (snapshot.state) { case 'paused': console.log('Upload is paused'); break; case 'running': console.log('Upload is running'); break; } }, (error) => { console.log("Error: ", error.message) }, () => { // Upload completed successfully, now we can get the download URL getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => { /*console.log('File available at', downloadURL); navigation.navigate({ name: 'Perfil', params: { changed: true }, merge: true, });*/ navigation.goBack(); }); } ); }
解决方案
优化Blob处理,避免内存溢出
Expo中通过fetch转Blob处理大图片时,容易出现内存问题。改用Expo提供的FileSystem模块读取文件为base64,再转成Uint8Array上传,减少内存占用:import * as FileSystem from 'expo-file-system'; const uploadImage = async (theImage) => { const base64 = await FileSystem.readAsStringAsync(theImage, { encoding: FileSystem.EncodingType.Base64 }); const uint8Array = Uint8Array.from(atob(base64), c => c.charCodeAt(0)); const storageRef = ref(storage, `images/${signedUser.uid}`); const uploadTask = uploadBytesResumable(storageRef, uint8Array); // 后续监听逻辑不变 }完善错误捕获与全局异常监听
当前错误回调只打印error.message,很多原生崩溃不会触发这个回调。在uploadBytesResumable外层包裹try-catch,同时添加全局异常监听:const uploadImage = async (theImage) => { try { // 原上传逻辑 } catch (e) { console.error('全局捕获错误:', e); } } // 全局监听(可放在App.js中) import * as ErrorRecovery from 'expo-error-recovery'; ErrorRecovery.setRecoveryProps({ lastAction: 'upload-image' });限制上传图片的尺寸与质量
大尺寸头像会导致上传过程中内存占用过高崩溃,上传前用Expo的ImageManipulator压缩图片:import * as ImageManipulator from 'expo-image-manipulator'; const compressImage = async (uri) => { const result = await ImageManipulator.manipulateAsync( uri, [{ resize: { width: 800 } }], // 限制宽度为800px { compress: 0.7, format: ImageManipulator.SaveFormat.JPEG } ); return result.uri; } // 在uploadImage中先压缩 const compressedUri = await compressImage(theImage); // 再用compressedUri处理上传确保上传任务的生命周期绑定
组件卸载时取消未完成的上传任务,避免内存泄漏导致崩溃:import { useEffect, useRef } from 'react'; const YourComponent = () => { const uploadTaskRef = useRef(null); const uploadImage = async (theImage) => { // ...原逻辑 uploadTaskRef.current = uploadTask; } useEffect(() => { return () => { if (uploadTaskRef.current) { uploadTaskRef.current.cancel(); } }; }, []); // ...其他组件逻辑 }更新依赖版本
检查并更新firebase、expo相关依赖到最新稳定版,部分旧版本存在已知的上传崩溃bug。
内容的提问来源于stack exchange,提问作者Noisemastering
相关产品推荐
相关产品推荐

