首页 > 解决方案 > 将异步传入图像上传到 Firebase 存储并等待所有要返回的 URL

问题描述

所以我是一个有角度的应用程序,它允许你将一些东西上传到云存储。比如说,标题、描述和一些图像。我将图像存储在 firebase 存储中,然后也将下载 URL 添加到 firebase。我正在构建一个方法,该方法接收一组文件,将图像裁剪为更可用的大小,然后将这些文件上传到 firebase 存储:

  uploadImages(images, title) {
    const urls: string[] = [];
    this.ng2ImgToolsService.resize(images, 700, 400).subscribe(result => {
      const uploadImage = new File([result], result.name);

      const path = `${title}/${Date.now()}_${uploadImage.name}`;
      const ref = this.storage.ref(path);
      const task = this.storage.upload(path, uploadImage);

      task.snapshotChanges().pipe(tap(), finalize(async () => {
        const url = await ref.getDownloadURL().toPromise;
        urls.push(url);
      }));  

    }, error => {
      console.log('resize failed, returning original images: ' + error)
    });

    return urls;
  }

这显然行不通。我还尝试将所有内容都包装在一个承诺中,这样我就可以做到;

const urls = await this.uploadImages(...);

但在这一点上,我在一个 promise 中的 observables 中有 observables,我必须承认我在我的头上。在我处理异步任务的时候,它们更加直截了当。

我又花了几天时间阅读 RxJs,但恐怕我自己无法弄清楚。

总而言之,这个函数需要返回一个url数组,我需要能够等待这个数组,所以基本上等待将所有内容上传到firestore,直到我有下载url。

编辑 好吧,在玩过 dockeryxk 答案后,我仍然遇到一些问题。首先,“ng2ImgToolsService.resize(images, 700, 400)” 函数接收一个图像数组,并返回一个承诺,一旦完成,将通过每个裁剪的图像,而不是一次所有的图像. 因此,如果我尝试将其包装在一个 Promise 中并解析此 observable 的输出,它将仅返回一个图像(以先裁剪的为准)。所以我必须等待 observable 完成发射数据?这可能吗?

其次,如果我确实将所有内容分开,以便一旦我有一组裁剪图像,我会将这些图像放入一个新函数中。在该函数中,我必须遍历该数组并一一上传。

 uploadImages2(images, title) {
    const observables = [];

    for (let image of images) {
      const path = `${title}/${Date.now()}_${image.name}`;
      const ref = this.storage.ref(path);
      const task = this.storage.upload(path, image);

      task.snapshotChanges().pipe(tap(), finalize(async () => {
        const url =  ref.getDownloadURL();
        observables.push(url);
      }));
    }

    return new Promise((resolve, reject) => {
      Observable.combineLatest(observables).subscribe(
        responses => {
          resolve(responses);
        },
        error => {
          console.log('error:', error);
          reject(error);
        }
      );
    });
  }

这确实会正确上传图像,但不会返回下载 url 数组。它基本上冻结在承诺的某个地方。

标签: angulartypescriptfirebaseasynchronousrxjs

解决方案


由于您对 Promise 更满意,因此您可以做一些事情,例如制作一组您的 observables,然后将它们组合起来。

const observables = [
  ...
];

return new Promise((resolve, reject) => {
  Observable.combineLatest(observables).subscribe(
    responses => {
      // responses array will be in the same order as requests
      console.log('responses:', responses);
      resolve(responses);
    },
    error => {
      console.log('error:', error);
      reject(error);
    }
  );
});

编辑:我没有意识到这个包是如何工作的,试试这样的:

uploadImages(images, title) {
  const urls: string[] = [];
  return new Promise((resolve, reject) => {
    this.ng2ImgToolsService.resize(images, 700, 400).subscribe(result => {
      const uploadImage = new File([result], result.name);

      const path = `${ title }/${ Date.now() }_${ uploadImage.name }`;
      const ref = this.storage.ref(path);
      const task = this.storage.upload(path, uploadImage);

      task.snapshotChanges().pipe(tap(), finalize(async () => {
        const url = await ref.getDownloadURL().toPromise();
        urls.push(url);
        // in this case, all images should be done
        if (urls.length === images.length) {
          resolve(urls);
        }
      }));

    }, error => {
      console.log('resize failed, returning original images: ' + error);
      reject(error);
    });
  });
}

推荐阅读