Angular 6 - 在上传 Firebase Storage 文件后获取下载 URL

8

我想实现的是一个简单的想法。

  1. 将文件上传到Firebase Storage

  2. 获取文件链接并在表单中插入它。

问题是,我无法获取下载链接。

当我上传文件时,它确实上传了,但我收到了这个错误消息:

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://dev59.com/fanka4cB1Zd3GeqPVuv8#57267424。 - Daniel Danielecki
对象未找到,因为downloadURL调用应在文件加载完成后进行。从uploadEvent函数中删除它,并按@dAxx_的建议将其添加到finalize函数中。 - PatrickS
6个回答

7
您应该给管道添加一个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();

1
StorageReference.getDownloadUrl() 返回一个 Task 而不是实际的 URL。您可能需要更新您的答案,以展示如何向任务添加完成监听器并从中获取实际的 URL。 - Frank van Puffelen
@FrankvanPuffelen 我正是遇到了同样的问题。这是一个好答案,但它并没有给我提供downloadURL。 - Rosenberg
@Rosenberg,如果你正在使用最新的Firebase,getDownloadURL()不是一个任务,而是一个Observable。我已经编辑了我的帖子来回答这个问题。祝你好运。 - dAxx_
啊...你正在使用AngularFire。那里确实会有一个observable,它也很好地摆脱/封装了异步行为。 - Frank van Puffelen

1

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

选项1: 您可以使用 concat 并推迟执行 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()完成后,使用switchMap转换为ref.getDownloadURL()
this.task.snapshotChanges().pipe(
  last(),  // emit the last element after task.snapshotChanges() completed
  switchMap(() => this.ref.getDownloadURL())
).subscribe(url => console.log(url))

1
this.angularFireStorage.upload("path_name", file).then(rst => {
        rst.ref.getDownloadURL().then(url => {
          console.log(url);
        })
      })

这是答案。不想调用两次。我的软件包版本是

"@angular/fire": "^5.1.2", "firebase": "^5.9.1"


0
@dAxx_的回答开头是正确的,但是嵌套订阅实际上是不好的做法。
请参考这个StackOverflow答案Is it good way to call subscribe inside subscribe? 现在,AngularFire 文档相当清晰,除了下面示例中使用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" alt="">

或者,你可以使用我上面提到的第一种解决方案,使用文件路径和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();
    }
   }

如果您需要在数据库中保存上传的图像的引用,那么您只需要关心文件路径。


-1

试试这个,对我有效

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

-1

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

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();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接