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

Angular 17集成Firebase Storage遇R3InjectorError问题求助

Angular 17集成Firebase Storage多图上传问题解决

错误原因

你遇到的NullInjectorError: No provider for InjectionToken angularfire2.app.options!,核心原因是API版本不兼容:

  • 你的UploadService使用了旧版兼容层的AngularFireStorage(来自@angular/fire/compat/storage)
  • 但app.config.ts里配置的是新版模块化的Firebase Storage提供者(provideStorage)

新旧API的依赖注入逻辑不同,导致旧版的AngularFireStorage找不到所需的配置提供者。


修复方案(推荐使用新版模块化API)

Angular 17是独立组件模式,推荐直接使用Firebase官方的模块化API,抛弃compat兼容层,步骤如下:

1. 调整UploadService代码

替换compat依赖,使用新的Storage API:

import { Injectable } from '@angular/core';
import { getStorage, ref, uploadBytesResumable, getDownloadURL } from 'firebase/storage';
import { Observable, forkJoin, from } from 'rxjs';
import { map, switchMap, finalize } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class UploadService {
  private basePath = '/uploads';
  imageUrls: string[] = [];
  private storage = getStorage(); // 初始化Storage实例

  uploadFiles(files: File[]): Observable<{ progress: Observable<number>, url: Observable<string> }[]> {
    return forkJoin(files.map(file => this.uploadFile(file)));
  }

  uploadFile(file: File): Observable<{ progress: Observable<number>, url: Observable<string> }> {
    const filePath = `${this.basePath}/${Date.now()}_${file.name}`; // 添加时间戳避免重名覆盖
    const storageRef = ref(this.storage, filePath);
    const uploadTask = uploadBytesResumable(storageRef, file);

    // 监听上传进度
    const progress$ = new Observable<number>(observer => {
      uploadTask.on('state_changed', snapshot => {
        const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
        observer.next(progress);
        if (progress === 100) {
          observer.complete();
        }
      });
    });

    // 获取下载URL
    const url$ = from(new Promise<string>((resolve, reject) => {
      uploadTask.on('state_changed', 
        () => {}, 
        (error) => reject(error),
        () => {
          getDownloadURL(uploadTask.snapshot.ref).then(url => {
            this.imageUrls.push(url);
            resolve(url);
          });
        }
      );
    }));

    return new Observable(observer => {
      observer.next({ progress: progress$, url: url$ });
      observer.complete();
    });
  }
}

2. 保持app.config.ts现有配置不变

你的app.config.ts里的provideFirebaseApp和provideStorage配置是正确的,无需修改:

import { ApplicationConfig, importProvidersFrom } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideHttpClient, withFetch } from '@angular/common/http';
import { firebaseConfig } from './firebase';
import { initializeApp } from 'firebase/app';
import { provideFirebaseApp } from '@angular/fire/app';
import { getStorage, provideStorage } from '@angular/fire/storage';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { FileDragNDropDirective } from './shared/directives/file-drag-ndrop.directive';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideHttpClient(withFetch()),
    importProvidersFrom(FontAwesomeModule),
    importProvidersFrom([
      provideFirebaseApp(() => initializeApp(firebaseConfig)),
      provideStorage(() => getStorage()),
    ]),
    FileDragNDropDirective
  ]
};

备选修复方案(继续使用compat兼容层)

如果暂时不想切换到新API,需要在app.config.ts中添加compat层的模块配置:

// 在app.config.ts中添加以下导入
import { AngularFireModule } from '@angular/fire/compat';
import { AngularFireStorageModule } from '@angular/fire/compat/storage';

// 修改providers数组,添加compat模块
export const appConfig: ApplicationConfig = {
  providers: [
    // ...其他现有提供者
    importProvidersFrom([
      provideFirebaseApp(() => initializeApp(firebaseConfig)),
      provideStorage(() => getStorage()),
      AngularFireModule.initializeApp(firebaseConfig), // 添加compat初始化
      AngularFireStorageModule // 添加compat存储模块
    ]),
    // ...其他现有提供者
  ]
};

注意:此方案仅作为过渡使用,长期建议迁移到模块化API。


功能优化建议

  1. 统一处理上传结果:在组件中调用服务时,可以同时监听进度和下载URL:
// 组件中调用示例
this.uploadService.uploadFiles(selectedFiles).subscribe(results => {
  results.forEach((result, index) => {
    // 监听单个文件上传进度
    result.progress.subscribe(progress => {
      console.log(`文件${index+1}上传进度: ${progress.toFixed(0)}%`);
    });
    // 获取单个文件下载URL
    result.url.subscribe(url => {
      console.log(`文件${index+1}下载URL:`, url);
    });
  });
});
  1. 错误处理:可以在上传逻辑中添加错误捕获,避免单个文件上传失败影响整体流程。

内容的提问来源于stack exchange,提问作者Aryan Singh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 07:40:56