Angular 6 - 上传后获取 Firebase 存储文件的下载 URL

Posted

技术标签:

【中文标题】Angular 6 - 上传后获取 Firebase 存储文件的下载 URL【英文标题】:Angular 6 - Getting download URL for Firebase Storage file after uploading 【发布时间】:2018-12-28 00:52:01 【问题描述】:

我在这里想要完成的是一个简单的想法。

    将文件上传到 Firebase 存储

    获取文件的链接并将其插入表单中。

问题是,我无法获取下载地址。

当我上传某些内容时,它确实会上传,但我收到以下错误消息:

Object  code_: "storage/object-not-found", message_: "Firebase Storage: Object 'rnmgm3vvpz' does not exist.", serverResponse_: "\n  \"error\": \n    \"code\": 404,\n    \"message\": \"Not Found.  Could not get object\"\n  \n", name_: "FirebaseError" 

这是要上传到component.ts的代码:

upload(event) 
  const id = Math.random().toString(36).substring(2);
  this.ref = this.afStorage.ref(id);
  this.task = this.ref.put(event.target.files[0]);
  this.uploadState = this.task.snapshotChanges().pipe(map(s => s.state));
  this.uploadProgress = this.task.percentageChanges();
  this.downloadURL = this.ref.getDownloadURL();

在component.html上:

<input type="file" (change)="upload($event)" accept=".png,.jpg" />

文件上传后如何获取downloadURL?

【问题讨论】:

试试这个答案在这个时间点是如何工作的https://***.com/a/57267424/11127383 找不到对象,因为应该在文件完成加载后进行 downloadURL 调用。如@dAxx_所述,将其从uploadEvent 函数中删除并添加到finalize 函数中 【参考方案1】:

您应该在管道中添加一个 finalize(),例如:

this.task.snapshotChanges().pipe(
  finalize(() => 
    this.downloadURL = this.ref.getDownloadURL(); // <-- Here the downloadURL is available.
  )
).subscribe();

在 finalize() 步骤中,downloadURL 可用,因此您可以从 ref 中异步获取他。 --更新 您说您使用的是 Angular 6,所以我假设您使用的是最新版本的 firebase。 他们将 getDownloadURL() 从 Task 更改为 Observable,因此要获取实际 URL,您只需订阅即可。

this.task.snapshotChanges().pipe(
  finalize(() => 
    this.ref.getDownloadURL().subscribe(url => 
      console.log(url); // <-- do what ever you want with the url..
    );
  )
).subscribe();

【讨论】:

StorageReference.getDownloadUrl() 返回 Task 而不是实际的 URL。您可能想要更新您的答案,以展示如何向任务添加完成侦听器并从中获取实际 URL。 @FrankvanPuffelen 正是我得到的。这是一个很好的答案,但它没有给我下载 URL。 @Rosenberg,如果您使用的是最新的 firebase,getDownloadURL() 不是任务,而是 Observable。我编辑我的帖子以获得答案。祝你好运.. 啊...您正在使用 AngularFire。它确实是一个可观察的,它也巧妙地摆脱/封装了异步行为。【参考方案2】:

ref.getDownloadURL() 必须在 task.snapshotChanges() 完成后调用。

选项1:可以使用concatdefer执行ref.getDownloadURL()

concat(
  this.task.snapshotChanges().pipe(ignoreElements()) // ignore snapshot changes
  defer(() => this.ref.getDownloadURL()) // execute getDownloadURL when snapshot changes completed
).subscribe(url => console.log(url));

选项 2:task.snapshotChanges() 完成后,switchMapref.getDownloadURL()

this.task.snapshotChanges().pipe(
  last(),  // emit the last element after task.snapshotChanges() completed
  switchMap(() => this.ref.getDownloadURL())
).subscribe(url => console.log(url))

【讨论】:

【参考方案3】:
this.angularFireStorage.upload("path_name", file).then(rst => 
        rst.ref.getDownloadURL().then(url => 
          console.log(url);
        )
      )

这就是答案。不想打两次电话。我的包版本是

"@angular/fire": "^5.1.2", “火力基地”:“^5.9.1”

【讨论】:

【参考方案4】:

@dAxx_ 答案的开头是正确的,但是嵌套订阅是非常糟糕的做法。

查看这个 *** 答案Is it good way to call subscribe inside subscribe?

现在,AngularFire docs 已经很清楚了,除了可能在以下示例中使用 getDownloadURL 管道

@Component(
 selector: 'app-root',
 template: `<img [src]="'users/davideast.jpg' | getDownloadURL" />`
 )
 export class AppComponent 

他们可能会提到您需要在相关模块中进行以下导入

import  GetDownloadURLPipeModule  from '@angular/fire/compat/storage';

...

imports: [
  GetDownloadURLPipeModule
 ],...

但是,如果您可能不需要下载 URL,那么简单的答案。

我不确定其背后的原因是什么,但 Firebase 团队已经为我们提供了足够的方式来显示上传的图片,而无需关心下载 URL 字符串。

首先,getDownloadURL 是一个 Observable,因此您可以像这样显示您的图像

...
finalize(() => 
      this.downloadURL = this.ref.getDownloadURL()
...


<img *ngIf="downloadURL | async as imgUrl"  [src]="imgUrl" >

或者,您可以使用我上面提到的第一个解决方案,使用文件路径和 getDownloadURL 管道

最后,您可以使用 Angular Fire 文档中的以下示例

   @Component(
   selector: 'app-root',
    template: `<img [src]="profileUrl | async" />`
    )
   export class AppComponent 
     
     profileUrl: Observable<string | null>;
     constructor(private storage: AngularFireStorage) 
       const ref = this.storage.ref('users/davideast.jpg');
       this.profileUrl = ref.getDownloadURL();
    
   

在这种情况下,您只需要关心文件路径,例如,如果您需要在数据库中保存对上传图像的引用。

【讨论】:

【参考方案5】:

试试这个,对我有用

task.snapshotChanges()
    .pipe(
          finalize(() => 
            this.downloadURL = fileRef.getDownloadURL();
            this.downloadURL.subscribe(downloadURLResponse => 
               console.log('downloadURL', downloadURLResponse);
            );
          ),
     )
    .subscribe();

【讨论】:

【参考方案6】:

这是一个包含所有导入的完整方法。

import  AngularFireStorage, AngularFireStorageReference, AngularFireUploadTask  from '@angular/fire/storage';
import finalize from 'rxjs/operators';

constructor(private afStorage:AngularFireStorage)  

yourfile:File;

onsubmit()
const id = Math.random().toString(36).substring(2);
const fileRef:AngularFireStorageReference=this.afStorage.ref("YourDir").child(id);
const task: AngularFireUploadTask =fileRef.put(this.yourfile);
task.snapshotChanges().pipe(
    finalize(() => 
        fileRef.getDownloadURL().subscribe(downloadURL => 
          this.profileurl=downloadURL;
            console.log(downloadURL);
        );
  )
).subscribe();

【讨论】:

以上是关于Angular 6 - 上传后获取 Firebase 存储文件的下载 URL的主要内容,如果未能解决你的问题,请参考以下文章

Angular 6:从 JSON 响应后端获取哈希表数据

启动后为 Angular 6 项目获取“错误 TS1005,TS1109”

Angular 6 项目刷新后显示 404 错误

如何在 Angular 6 中上传之前验证文件

使用 Angular 6 将文件上传到 Web Api C#

如何使用添加按钮在 Angular 6 中上传多个文件?