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

React Native iOS应用:RN与嵌入原生模块共用Firebase Storage上传方案

实现React Native iOS端与原生模块共用Firebase Storage上传的方案

1. 确保Firebase初始化的一致性

  • 确认RN项目已集成@react-native-firebase/storage,同时iOS原生项目通过CocoaPods引入Firebase/Storage依赖(RN Firebase集成后通常已自动配置,可检查Podfile确认)。
  • 原生模块不要重复初始化Firebase,直接复用RN侧启动时初始化的默认实例:
    // Objective-C 示例
    FIRApp *defaultApp = [FIRApp defaultApp];
    if (!defaultApp) {
        // 极端场景下RN未初始化时才手动配置,优先依赖RN侧初始化
        [FIRApp configure];
    }
    
    Swift版本:
    guard FirebaseApp.defaultApp() != nil else {
        FirebaseApp.configure()
        return
    }
    

2. React Native端上传实现

直接使用@react-native-firebase/storage标准API完成上传:

import storage from '@react-native-firebase/storage';

const uploadViaRN = async (localFilePath, storageBucketPath) => {
  const storageRef = storage().ref(storageBucketPath);
  const uploadTask = storageRef.putFile(localFilePath);

  // 监听上传进度
  uploadTask.on('state_changed', snapshot => {
    const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
    console.log(`RN上传进度: ${progress.toFixed(2)}%`);
  });

  try {
    await uploadTask;
    const downloadUrl = await storageRef.getDownloadURL();
    return downloadUrl;
  } catch (error) {
    console.error('RN上传失败:', error.message);
    throw error;
  }
};

3. 原生iOS模块上传实现

在自定义原生模块中调用Firebase Storage原生API完成上传:

Objective-C示例

#import <FirebaseStorage/FirebaseStorage.h>
#import <React/RCTBridgeModule.h>

@interface CustomStorageModule : NSObject <RCTBridgeModule>
@end

@implementation CustomStorageModule

RCT_EXPORT_MODULE();

RCT_EXPORT_METHOD(uploadViaNative:(NSString *)localPath
                  storagePath:(NSString *)storagePath
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject) {
  FIRStorage *storage = [FIRStorage storage];
  FIRStorageReference *storageRef = [storage referenceWithPath:storagePath];
  
  NSURL *fileUrl = [NSURL fileURLWithPath:localPath];
  FIRStorageUploadTask *uploadTask = [storageRef putFile:fileUrl metadata:nil completion:^(FIRStorageMetadata * _Nullable metadata, NSError * _Nullable error) {
    if (error) {
      reject(@"NATIVE_UPLOAD_FAILED", @"原生上传出错", error);
      return;
    }
    [storageRef downloadURLWithCompletion:^(NSURL * _Nullable URL, NSError * _Nullable error) {
      if (error) {
        reject(@"GET_URL_FAILED", @"获取下载链接失败", error);
        return;
      }
      resolve(URL.absoluteString);
    }];
  }];

  // 监听上传进度
  [uploadTask observeStatus:FIRStorageTaskStatusProgress handler:^(FIRStorageTaskSnapshot *snapshot) {
    double progress = (double)snapshot.progress.completedUnitCount / snapshot.progress.totalUnitCount;
    NSLog(@"原生上传进度: %.2f%%", progress * 100);
  }];
}

@end

Swift示例

import FirebaseStorage
import React

@objc(CustomStorageModule)
class CustomStorageModule: NSObject, RCTBridgeModule {
  static func moduleName() -> String {
    return "CustomStorageModule"
  }

  static func requiresMainQueueSetup() -> Bool {
    return true
  }

  @objc func uploadViaNative(_ localPath: String,
                             storagePath: String,
                             resolver resolve: @escaping RCTPromiseResolveBlock,
                             rejecter reject: @escaping RCTPromiseRejectBlock) {
    let storage = Storage.storage()
    let storageRef = storage.reference(withPath: storagePath)
    
    let fileUrl = URL(fileURLWithPath: localPath)
    let uploadTask = storageRef.putFile(from: fileUrl, metadata: nil) { _, error in
      guard error == nil else {
        reject("NATIVE_UPLOAD_FAILED", "原生上传出错", error)
        return
      }
      storageRef.downloadURL { url, error in
        guard let downloadUrl = url?.absoluteString else {
          reject("GET_URL_FAILED", "获取下载链接失败", error)
          return
        }
        resolve(downloadUrl)
      }
    }

    // 监听上传进度
    uploadTask.observe(.progress) { snapshot in
      guard let progress = snapshot.progress else { return }
      let progressPercent = Double(progress.completedUnitCount) / Double(progress.totalUnitCount) * 100
      print("原生上传进度: \(progressPercent.toFixed(2))%")
    }
  }
}

extension Double {
  func toFixed(_ places: Int) -> String {
    return String(format: "%.\(places)f", self)
  }
}

4. 核心注意事项

  • 权限统一:RN和原生端共用同一Firebase项目,确保Storage安全规则对两端上传路径、操作权限的限制一致。
  • 路径规范:两端使用的存储桶路径格式要统一(比如是否带开头斜杠),避免出现路径解析错误。
  • 错误处理:两端都要覆盖网络异常、文件不存在、权限不足等场景的错误捕获与反馈。
  • 禁止重复初始化:必须保证RN侧优先完成Firebase初始化,原生模块复用默认实例,否则会导致配置冲突。

内容的提问来源于stack exchange,提问作者VIKASH SR

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 23:19:01