React Native iOS设备上传图片至Firebase Storage失败求助
React Native iOS真机上传Firebase Storage失败问题
问题描述
在React Native应用中将图片上传至Firebase Storage时,Android设备和iOS模拟器均可正常运行,但iOS真机上传失败,报错信息如下:
NativeFirebaseError: [storage/unknown] An unknown error has occurred. (underlying reason 'Can not finalize upload. Current size is 0. Expected final size is 201112.')
使用的库版本:react-native-image-crop-picker 0.38.0
选择图片代码
const choosePhotoFromLibrary = () => { ImagePicker.openPicker({ width: 300, height: 300, cropping: true }).then((image) => { const imageUri = Platform.OS === 'ios' ? image.sourceURL : image.path console.log(imageUri) }) .catch((e) =>{ console.log(e) }) }
Firebase配置详情
Podfile配置
require_relative '../node_modules/react-native/scripts/react_native_pods' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' $RNFirebaseAsStaticFramework = true platform :ios, '12.4' install! 'cocoapods', :deterministic_uuids => false use_frameworks! :linkage => :static target 'zaater' do config = use_native_modules! #Storage troubleshooting pod 'Firebase/Core' pod 'Firebase/Auth' pod 'Firebase/Firestore' pod 'Firebase/Storage' # Flags change depending on the env values.xx flags = get_default_flags() use_react_native!( :path => config[:reactNativePath], # Hermes is now enabled by default. Disable by setting this flag to false. # Upcoming versions of React Native may rely on get_default_flags(), but # we make it explicit here to aid in the React Native upgrade process. :hermes_enabled => true, :fabric_enabled => flags[:fabric_enabled], # Enables Flipper. # # Note that if you have use_frameworks! enabled, Flipper will not work and # you should disable the next line. # :flipper_configuration => FlipperConfiguration.enabled, # An absolute path to your application root. :app_path => "#{Pod::Config.instance.installation_root}/.." ) target 'zaaterTests' do inherit! :complete # Pods for testing end post_install do |installer| react_native_post_install( installer, # Set `mac_catalyst_enabled` to `true` in order to apply patches # necessary for Mac Catalyst builds :mac_catalyst_enabled => false ) __apply_Xcode_12_5_M1_post_install_workaround(installer) end end
AppDelegate.mm配置
#import <Firebase.h> if ([FIRApp defaultApp] == nil) { [FIRApp configure]; }
已将GoogleService-info.plist文件添加至iOS目录(与Podfile同级),同时也添加到了Xcode项目中。
图片上传函数
const uploadImage = async (image) => { try { const uploadUri = image let filename = uploadUri.substring(uploadUri.lastIndexOf('/') + 1) // Add timestamp to File Name const extension = filename.split('.').pop(); const name = filename.split('.').slice(0, -1).join('.') filename = name + Date.now() + '.' + extension //Store image const storageRef = storage().ref(`users/${idx}/${filename}`) const task = storageRef.putFile(uploadUri) //get image URL after being downloaded to firebase storage await task const url = await storageRef.getDownloadURL() return url } catch (e) { console.log(e) return null } }
解决方案
1. 修正iOS真机的图片URI处理
iOS真机上image.sourceURL是ph://格式的照片库URI,Firebase的putFile无法直接读取这种格式的文件,需改用image.path(沙盒本地路径):
const choosePhotoFromLibrary = () => { ImagePicker.openPicker({ width: 300, height: 300, cropping: true, includeBase64: false, mediaType: 'photo' }).then((image) => { // iOS真机统一使用image.path const imageUri = image.path console.log(imageUri) uploadImage(imageUri) }) .catch((e) =>{ console.log(e) }) }
2. 配置iOS照片库权限
确保Info.plist中添加照片库访问权限声明:
<key>NSPhotoLibraryUsageDescription</key> <string>需要访问您的照片库以上传图片</string> <key>NSPhotoLibraryAddUsageDescription</key> <string>需要访问您的照片库以上传图片</string>
3. 改用Base64格式上传(备选方案)
如果沙盒路径仍无法读取,可将图片转为Base64后用putString上传:
const uploadImage = async (image) => { try { let uploadUri = image let filename = uploadUri.substring(uploadUri.lastIndexOf('/') + 1) const extension = filename.split('.').pop(); const name = filename.split('.').slice(0, -1).join('.') filename = name + Date.now() + '.' + extension // 处理iOS的ph://格式URI if (Platform.OS === 'ios' && uploadUri.startsWith('ph://')) { const base64Data = await ImagePicker.readFile(uploadUri) const storageRef = storage().ref(`users/${idx}/${filename}`) const task = storageRef.putString(base64Data, 'base64', { contentType: `image/${extension}` }) await task const url = await storageRef.getDownloadURL() return url } else { // 原有文件路径上传逻辑 const storageRef = storage().ref(`users/${idx}/${filename}`) const task = storageRef.putFile(uploadUri) await task const url = await storageRef.getDownloadURL() return url } } catch (e) { console.log(e) return null } }
4. 检查Firebase Storage规则
临时开放测试规则(上线后需修改为安全规则):
rules_version = '2'; service firebase.storage { match /b/{bucket}/o { match /{allPaths=**} { allow read, write: if true; } } }
内容的提问来源于stack exchange,提问作者Khaled Ahmed
相关产品推荐
相关产品推荐

