Angular 16集成Firebase 9报错:Service storage is not available
问题背景
使用Angular v16项目,搭配@angular/fire: ^7.6.1和手动安装的firebase: ^9.0.0,已完成Firebase初始化。联系表单提交时,上传附件到Firebase Storage出现错误:
Error: Service storage is not available
at Provider.getImmediate
at getStorage
at ContactUsComponent.onSubmit
执行npm ls @firebase/app发现依赖冲突,项目中同时存在两个版本的Firebase核心包:
@angular/fire@7.6.1自带firebase@9.23.0,依赖@firebase/app@0.9.13/0.9.14- 手动安装的
firebase@9.0.0依赖@firebase/app@0.7.0
问题根源
多个版本的@firebase/app共存导致Firebase模块初始化混乱,Storage服务无法被正确识别和加载。Angular Fire已经内置了兼容版本的Firebase SDK,无需单独安装firebase包。
解决方案
1. 清理冲突依赖
卸载手动安装的firebase包,保留Angular Fire自带的兼容版本:
npm uninstall firebase npm install
2. 统一Firebase导入来源
确保所有Firebase相关的导入都来自@angular/fire或其细分模块,避免直接从firebase包导入,防止版本混淆。
代码调整建议
组件中依赖注入Storage实例
在contact-us.component.ts中,通过Angular依赖注入获取Storage实例,替代手动调用getApp和getStorage:
import { Component } from '@angular/core'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { Storage } from '@angular/fire/storage'; import { ref, uploadBytesResumable, getDownloadURL } from '@firebase/storage'; import { ContactService } from './contact.service'; import { tap, catchError, throwError } from 'rxjs'; @Component({ selector: 'app-contact-us', templateUrl: './contact-us.component.html', styleUrls: ['./contact-us.component.css'] }) export class ContactUsComponent { contactForm: FormGroup; loading = false; messageSuccessMessage = false; messageFailureMessage = false; emailId: string; // 假设已定义 constructor( private fb: FormBuilder, private storage: Storage, private contactService: ContactService ) { this.contactForm = this.fb.group({ fullName: ['', Validators.required], email: ['', [Validators.required, Validators.email]], phone: [''], subject: ['', Validators.required], message: ['', Validators.required], attachment: [''] }); } onFileChange(event: any) { if (event.target.files.length > 0) { const file = event.target.files[0]; if (file.size > 5242880) { // 5MB限制 alert('文件过大!'); this.contactForm.get('attachment')?.setErrors({ 'incorrect': true }); } else { this.contactForm.get('attachment')?.setValue(file); } } } onSubmit() { const value = this.contactForm.value; const file = value.attachment; const newEmail: Partial<Contact> = { fullName: value.fullName, email: value.email, phone: value.phone, subject: value.subject, message: value.message, attachmentUrl: '', }; if (this.contactForm.valid) { this.loading = true; // 使用唯一路径存储文件,避免覆盖 const fileRef = ref(this.storage, `contact-attachments/${Date.now()}-${file.name}`); const uploadTask = uploadBytesResumable(fileRef, file); uploadTask.then((snapshot) => { return getDownloadURL(snapshot.ref); }).then((downloadURL) => { newEmail.attachmentUrl = downloadURL; this.contactService.sendContact(newEmail, this.emailId).pipe( tap(() => { this.loading = false; this.contactForm.reset(); this.messageSuccessMessage = true; setTimeout(() => this.messageSuccessMessage = false, 3000); }), catchError((error) => { this.loading = false; this.messageFailureMessage = true; setTimeout(() => this.messageFailureMessage = false, 3000); return throwError(() => error); }) ).subscribe(); }).catch((error) => { console.error('上传失败:', error); this.loading = false; this.messageFailureMessage = true; setTimeout(() => this.messageFailureMessage = false, 3000); }); } else { this.contactForm.markAllAsTouched(); } } } interface Contact { fullName: string; email: string; phone?: string; subject: string; message: string; attachmentUrl: string; }
确认AppModule配置
当前app.module.ts中的Storage配置无需修改,确保provideStorage正确注册即可:
provideStorage(() => getStorage()),
验证
完成上述步骤后,重新启动项目,测试联系表单提交功能,确认Storage服务正常加载,附件能成功上传并获取downloadURL。
内容的提问来源于stack exchange,提问作者Robgit28

