将异步传入图像上传到Firebase存储,并等待所有URL返回

时间:2019-06-26 16:52:45

标签: angular typescript firebase asynchronous rxjs

所以我是一个有角度的应用程序,它允许您将一些内容上传到Cloud Storage。例如标题,说明和一些图片。我将图像存储在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(...);

但是,在这一点上,我对诺言中的可观察性有一定的了解,我必须承认自己已经过时了。我处理异步任务的时间更加直截了当。

我花了几天多的时间来阅读RxJ,但我恐怕无法自行解决。

总而言之,此函数需要返回一个URL数组,我需要能够等待该数组,因此基本上等待所有内容上传到Firestore直到我拥有下载URL。

修改 玩完ockereryxk答案后,我仍然遇到一些问题。首先,“ ng2ImgToolsService.resize(images,700,400)”函数接收一个图像数组,并返回一个承诺,一旦完成,它将立即遍历每个裁剪的图像,而不是全部。因此,如果我尝试将其包装在Promise中并解析此可观察的输出,则它只会返回一个图像(以先裁剪的图像为准)。因此,我必须等待可观察对象完成发射数据?这可能吗?

第二,如果我确实将所有内容分割开,以便一旦我拥有一系列裁剪的图像,就可以将这些图像放入新功能中。在该函数中,我必须遍历该数组,然后将它们一个接一个地上传。

 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数组。它基本上冻结在诺言中。

2 个答案:

答案 0 :(得分:0)

由于您对诺言更加满意,因此您可以执行一些操作,例如将可观察对象阵列化,然后将它们组合起来。

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

答案 1 :(得分:0)

好的,我已经弄清楚了。非常感谢ockereryxk提供的通用解决方案。分开我的职能并基本上执行两次Dockleryxk的战术最终成功了:

调整传入图像大小的功能:

resizeImages(images: File[], width: number, height: number) {

    /*
    this function takes in an array of images, a width and a height.
    if the array contains 'png' or 'jpg' files. it will scale down the images to either the width or the height given.
    if the array contains other image files it will just return them.
    */

    const toResize: File[] = [];
    const resized: File[] = [];

    for (const file of images) {
      if (this.getFileExtension(file) === 'png' || this.getFileExtension(file) === 'jpg') {
        toResize.push(file);
      } else {
        resized.push(file);
      }
    }

    return new Promise((resolve, reject) => {
      if (toResize.length > 0) {
        this.ng2ImgToolsService.resize(toResize, width, height).subscribe(response => {
          resized.push(this.blobToFile(response, response.name));

          if (resized.length === images.length) {
            resolve(resized);
          }
        }, error => {
          console.log('error:', error);
          reject(error);
        });
      } else {
        resolve(resized);
      }
    });
  }

上传这些图像的功能:

  uploadImages(images: File[], title: string) {
    return new Promise((resolve, reject) => {
      const urls: string[] = [];
      for (const file of images) {
        const path = `${title}/${Date.now()}_${file.name}`;
        const ref = this.storage.ref(path);
        const task = this.storage.upload(path, file);

        task.snapshotChanges().pipe(
          finalize(() => {
            ref.getDownloadURL().subscribe(url => {
              urls.push(url);

              if (images.length === urls.length) {
                resolve(urls);
              }
            });
          })
        ).subscribe();
      }
    });
  }

,然后在您的代码中使用它们:

const imgs: any = await this.resizeImages(data.images, 700, 400);
console.log(imgs);
const urls: any = await this.uploadImages(imgs, data.title);
console.log(urls);

我可以说这不是最好的解决方案,我等待了不必要的时间,这可能会加快速度。因此,如果有人有更好的解决方案,我希望能听到。但这行得通!