React Native Expo上传图片至Firebase Storage异常:仅显示白框
问题:Expo React Native 上传图片至Firebase Storage显示空白白框
我用Expo开发React Native应用,尝试将图片上传至Firebase Storage,按照官方文档实现后,上传过程无报错,但Firebase中仅显示空白白框,预览也出现错误。
上传后的Firebase显示:
预览时的错误:
我的完整代码:
import React, { useState } from 'react'; import { Image, View, Text, Button, ActivityIndicator } from 'react-native'; import * as ImagePicker from 'expo-image-picker'; import { app } from './firebaseConfig'; import { getStorage, ref, uploadBytesResumable, getDownloadURL } from "firebase/storage"; import 'firebase/storage'; const App = () => { const [image, setImage] = useState(null); const [uploading, setUploading] = useState(false); const pickImage = async () => { try { let result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images, allowsEditing: true, aspect: [4, 3], quality: 1, }); if (!result.canceled) { console.log(result.assets[0].uri) setImage(result.assets[0].uri); } } catch (E) { console.log(E); } }; const storage = getStorage(app); // Create the file metadata /** @type {any} */ const metadata = { contentType: 'image/jpeg' }; // Upload file and metadata to the object 'images/mountains.jpg' const storageRef = ref(storage, 'images/' + Date.now()); const uploadTask = uploadBytesResumable(storageRef, image, metadata); console.log(storageRef) // 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) => { // A full list of error codes is available at // https://firebase.google.com/docs/storage/web/handle-errors switch (error.code) { case 'storage/unauthorized': // User doesn't have permission to access the object break; case 'storage/canceled': // User canceled the upload break; // ... case 'storage/unknown': // Unknown error occurred, inspect error.serverResponse break; } }, () => { // Upload completed successfully, now we can get the download URL getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => { console.log('File available at', downloadURL); }); } ); return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Button title="Pick an image from camera roll" onPress={pickImage} /> {image && ( <> <Image source={{ uri: image }} style={{ width: 200, height: 200 }} /> </> )} {uploading && <ActivityIndicator />} </View> ); }; export default App;
问题原因与修复方案
核心问题
你的代码存在两个关键错误:
- 上传逻辑执行时机错误:上传代码直接写在组件函数体内,组件每次渲染都会自动执行上传,此时
image大概率是null,导致上传了空内容到Firebase。 - 未处理本地文件URI:
ImagePicker返回的是本地文件路径(URI),而uploadBytesResumable需要的是Blob或File对象,直接传URI无法正确读取图片数据。
修复步骤
- 封装上传函数,按需触发:将上传逻辑放到单独的函数中,只有在用户选择图片后再调用该函数。
- 转换本地URI为Blob:通过
fetch获取本地文件并转换为Blob对象,再传给Firebase的上传接口。
修复后的完整代码
import React, { useState } from 'react'; import { Image, View, Button, ActivityIndicator } from 'react-native'; import * as ImagePicker from 'expo-image-picker'; import { app } from './firebaseConfig'; import { getStorage, ref, uploadBytesResumable, getDownloadURL } from "firebase/storage"; const App = () => { const [image, setImage] = useState(null); const [uploading, setUploading] = useState(false); const pickImage = async () => { try { let result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images, allowsEditing: true, aspect: [4, 3], quality: 1, }); if (!result.canceled) { const selectedImageUri = result.assets[0].uri; setImage(selectedImageUri); // 选择图片后触发上传 await uploadImage(selectedImageUri); } } catch (E) { console.log(E); } }; const uploadImage = async (imageUri) => { setUploading(true); try { // 将本地URI转换为Blob对象 const response = await fetch(imageUri); const blob = await response.blob(); const storage = getStorage(app); const storageRef = ref(storage, 'images/' + Date.now()); const metadata = { contentType: blob.type || 'image/jpeg' }; const uploadTask = uploadBytesResumable(storageRef, blob, metadata); uploadTask.on('state_changed', (snapshot) => { 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.error('Upload error:', error); setUploading(false); switch (error.code) { case 'storage/unauthorized': console.log('无权限访问存储对象'); break; case 'storage/canceled': console.log('用户取消上传'); break; case 'storage/unknown': console.log('未知错误,请检查服务端响应'); break; } }, () => { getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => { console.log('文件可访问地址:', downloadURL); setUploading(false); }); } ); } catch (error) { console.error('转换Blob或上传失败:', error); setUploading(false); } }; return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Button title="从相册选择图片" onPress={pickImage} /> {image && ( <Image source={{ uri: image }} style={{ width: 200, height: 200, marginTop: 20 }} /> )} {uploading && <ActivityIndicator size="large" style={{ marginTop: 20 }} />} </View> ); }; export default App;
额外注意事项
- Storage安全规则:开发阶段可临时设置宽松规则允许上传,正式环境需配置严格的权限控制,避免恶意上传。
- 动态处理文件类型:代码中通过
blob.type自动获取文件类型,适配JPG、PNG等不同格式的图片。
内容的提问来源于stack exchange,提问作者TheDonHub
相关产品推荐
相关产品推荐

