如果我有存储位置,如何获取下载URI

9 浏览
0 Comments

如果我有存储位置,如何获取下载URI

使用 Firebase 函数上传文件到 Firebase 存储后,我想获取文件的下载 URL。

我有这个:

...
return bucket
    .upload(fromFilePath, {destination: toFilePath})
    .then((err, file) => {
        // Get the download url of file
    });

该文件对象有很多参数,其中一个被命名为mediaLink。然而,如果我尝试访问此链接,我会得到以下错误:

匿名用户无法访问对象的storage.objects.get权限...

可以有人告诉我如何获取公共下载 URL?

谢谢

admin 更改状态以发布 2023年5月20日
0
0 Comments

本答案将总结上传文件到Google/Firebase Cloud Storage后获得下载URL的选项。有三种类型的下载URL:

  1. 令牌下载URL,它们是持久的并具有安全功能
  2. 已签名的下载URL,它们是临时的并具有安全功能
  3. 公共下载URL,它们是持久的但缺乏安全性

有两种方法可以获得令牌下载URL。已签名和公共下载URL每个只有一种获取方法。

令牌URL方法#1:从Firebase Storage控制台

您可以从Firebase Storage控制台获取下载URL:

enter image description here

下载URL如下所示:

https://firebasestorage.googleapis.com/v0/b/languagetwo-cd94d.appspot.com/o/Audio%2FEnglish%2FUnited_States-OED-0%2Fabout.mp3?alt=media&token=489c48b3-23fb-4270-bd85-0a328d2808e5

第一个部分是文件的标准路径。最后是令牌。该下载URL是永久的,即它不会过期,尽管您可以撤销它。

令牌URL方法#2:从前端

文档告诉我们要使用getDownloadURL()

let url = await firebase.storage().ref('Audio/English/United_States-OED-' + i +'/' + $scope.word.word + ".mp3").getDownloadURL();

这会获取与您可以从Firebase Storage控制台获取的相同的下载URL。此方法很简单,但需要您知道文件的路径,在我的应用程序中有点困难。您可以从前端上传文件,但这会将您的凭据暴露给下载您的应用程序的任何人。因此,对于大多数项目,您将希望从Cloud Functions上传您的文件,然后获取下载URL并将其与有关文件的其他数据一起保存到数据库中。

当我从Cloud Function将文件写入Storage时,我找不到获取令牌下载URL的方法(因为我找不到告诉前端文件已写入Storage的方法),但对我有效的方法是将文件写入可公开使用的URL,将可公开使用的URL写入Firebase,然后当我的Angular前端从Firebase获取下载URL时也运行getDownloadURL(),它有令牌,然后将Firestore中的下载URL与令牌下载URL进行比较,如果它们不匹配,则在Firestore中更新令牌下载URL(替换可公开使用的URL)。这只将您的文件公开一次。

这比听起来容易得多。以下代码遍历Storage下载URL数组,并将可公开使用的下载URL替换为令牌下载URL。

const storage = getStorage();
var audioFiles: string[] = [];
if (this.pronunciationArray[0].pronunciation != undefined) {
          for (const audioFile of this.pronunciationArray[0].audioFiles) { // for each audio file in array
            let url = await getDownloadURL(ref(storage, audioFile)); // get the download url with token
            if (audioFile !== url) { // download URLs don't match
              audioFiles.push(url);
            } // end inner if
          }; // end for of loop
          if (audioFiles.length > 0) { // update Firestore only if we have new download URLs
            await updateDoc(doc(this.firestore, 'Dictionaries/' + this.l2Language.long + '/Words/' + word + '/Pronunciations/', this.pronunciationArray[0].pronunciation), {
              audioFiles: audioFiles
            });
          }
} // end outer if

您可能会想:“我将从Cloud Function将Storage位置返回给我的前端,然后在Firestore使用此位置与getDownloadURL()一起编写令牌下载URL。”这不会生效,因为Cloud Functions仅能返回同步结果。异步操作返回null

“没问题,”你说,“我可以在 Storage 上设置 Observer,从 Observer 获取位置,然后使用该位置和 getDownloadURL() 将令牌下载 URL 写入 Firestore。” 没戏。Firestore 有观察者,而 Storage 没有观察者。

“那么,”你说,“我从前端调用 listAll(),获取所有存储文件的列表,然后为每个文件调用 metadata,并提取每个文件的下载 URL 和令牌,然后将它们写入 Firestore?” 很好的尝试,但是存储元数据不包括下载 URL 或令牌。

签名 URL 方法 #1: 用于临时下载 URL 的 getSignedUrl()

getSignedUrl() 在 Cloud Function 中可以很容易地使用:

  function oedPromise() {
    return new Promise(function(resolve, reject) {
      http.get(oedAudioURL, function(response) {
        response.pipe(file.createWriteStream(options))
        .on('error', function(error) {
          console.error(error);
          reject(error);
        })
        .on('finish', function() {
          file.getSignedUrl(config, function(err, url) {
            if (err) {
              console.error(err);
              return;
            } else {
              resolve(url);
            }
          });
        });
      });
    });
  }

签名的下载 URL 如下:

https://storage.googleapis.com/languagetwo-cd94d.appspot.com/Audio%2FSpanish%2FLatin_America-Sofia-Female-IBM%2Faqu%C3%AD.mp3?GoogleAccessId=languagetwo-cd94d%40appspot.gserviceaccount.com&Expires=4711305600&Signature=WUmABCZIlUp6eg7dKaBFycuO%2Baz5vOGTl29Je%2BNpselq8JSl7%2BIGG1LnCl0AlrHpxVZLxhk0iiqIejj4Qa6pSMx%2FhuBfZLT2Z%2FQhIzEAoyiZFn8xy%2FrhtymjDcpbDKGZYjmWNONFezMgYekNYHi05EPMoHtiUDsP47xHm3XwW9BcbuW6DaWh2UKrCxERy6cJTJ01H9NK1wCUZSMT0%2BUeNpwTvbRwc4aIqSD3UbXSMQlFMxxWbPvf%2B8Q0nEcaAB1qMKwNhw1ofAxSSaJvUdXeLFNVxsjm2V9HX4Y7OIuWwAxtGedLhgSleOP4ErByvGQCZsoO4nljjF97veil62ilaQ%3D%3D

签名 URL 有一个过期日期和长签名。命令行 gsutil signurl -d 的文档说明签名 URL 是临时的,其默认过期时间为一小时,最长过期时间为七天。

我要在这里抱怨一下,getSignedUrl 的文档从来没有说过你的签名 URL 将在一周后过期。文档代码将 3-17-2025 作为过期日期,表明您可以将过期时间设定为未来的年份。我的应用程序工作得非常完美,然后一周后就崩溃了。错误消息说签名不匹配,而不是下载 URL 已过期。我对代码进行了各种更改,一切正常... 直到一周后,一切都崩溃了。这样的挫败感持续了一个多月。这个 3-17-2025 日期是内幕玩笑吗?就像在视线外的妖精的金币会消失一样,到了两周后,一个未来的圣帕特里克节到期日期就会消失,就在你以为你的代码没有 bug 的时候。

公共 URL 方法 #1:使您的文件公开可用

您可以按照文档中所述的方法设置文件的权限为公共读取。这可以从 Cloud Storage Browser 或您的 Node 服务器上完成。您可以使一个文件、一个目录或您的整个存储数据库变为公共数据库。以下是 Node 的代码:

var webmPromise = new Promise(function(resolve, reject) {
      var options = {
        destination: ('Audio/' + longLanguage + '/' + pronunciation + '/' + word + '.mp3'),
        predefinedAcl: 'publicRead',
        contentType: 'audio/' + audioType,
      };
      synthesizeParams.accept = 'audio/webm';
      var file = bucket.file('Audio/' + longLanguage + '/' + pronunciation + '/' + word + '.webm');
      textToSpeech.synthesize(synthesizeParams)
      .then(function(audio) {
        audio.pipe(file.createWriteStream(options));
      })
      .then(function() {
        console.log("webm audio file written.");
        resolve();
      })
      .catch(error => console.error(error));
    });

在 Cloud Storage Browser 中结果如下所示:

enter image description here

然后,任何人都可以使用标准路径下载您的文件:

https://storage.googleapis.com/languagetwo-cd94d.appspot.com/Audio/English/United_States-OED-0/system.mp3

另一种使文件公开的方法是使用makePublic()方法。我无法使其工作,很难正确获取存储桶和文件路径。

有一种有趣的替代方法是使用访问控制列表。您可以仅将文件提供给您列出的用户,或使用authenticatedRead让登录了 Google 帐户的任何人都可以使用该文件。如果有一个“任何使用 Firebase Auth 登录我的应用的人”的选项,我会使用它,因为它将仅限制访问来源于我的用户。

已弃用:使用 firebaseStorageDownloadTokens 构建自己的下载 URL

有几个答案描述了一个未记录在官方文档中的 Google Storage 对象属性firebaseStorageDownloadTokens。这从来不是官方的 Google Cloud Storage 功能,它已不再起作用。以下是它的使用方法。

您告诉 Storage 您想要使用的令牌。然后您使用uuid Node 模块生成令牌。只需四行代码,您就可以构建自己的下载 URL,与您从控制台或getDownloadURL()获得的下载 URL 相同。这四行代码是:

const uuidv4 = require('uuid/v4');
const uuid = uuidv4();
metadata: { firebaseStorageDownloadTokens: uuid }
https://firebasestorage.googleapis.com/v0/b/" + bucket.name + "/o/" + encodeURIComponent('Audio/' + longLanguage + '/' + pronunciation + '/' + word + '.webm') + "?alt=media&token=" + uuid);

以下是上下文中的代码:

var webmPromise = new Promise(function(resolve, reject) {
  var options = {
    destination: ('Audio/' + longLanguage + '/' + pronunciation + '/' + word + '.mp3'),
    contentType: 'audio/' + audioType,
    metadata: {
      metadata: {
        firebaseStorageDownloadTokens: uuid,
      }
    }
  };
      synthesizeParams.accept = 'audio/webm';
      var file = bucket.file('Audio/' + longLanguage + '/' + pronunciation + '/' + word + '.webm');
      textToSpeech.synthesize(synthesizeParams)
      .then(function(audio) {
        audio.pipe(file.createWriteStream(options));
      })
      .then(function() {
        resolve("https://firebasestorage.googleapis.com/v0/b/" + bucket.name + "/o/" + encodeURIComponent('Audio/' + longLanguage + '/' + pronunciation + '/' + word + '.webm') + "?alt=media&token=" + uuid);
      })
      .catch(error => console.error(error));
});

这不是打印错误 - 您必须双层嵌套metadata:内的firebaseStorageDownloadTokens

0
0 Comments

您需要使用getSignedURL通过@google-cloud/storage NPM模块生成带签名的URL。

例如:

const gcs = require('@google-cloud/storage')({keyFilename: 'service-account.json'});
// ...
const bucket = gcs.bucket(bucket);
const file = bucket.file(fileName);
return file.getSignedUrl({
  action: 'read',
  expires: '03-09-2491'
}).then(signedUrls => {
  // signedUrls[0] contains the file's public URL
});

您需要使用您的服务帐户凭据初始化@google-cloud/storage,因为应用程序默认凭据不足。

更新:现在可以通过Firebase Admin SDK访问Cloud Storage SDK,它将作为包装器围绕@ google-cloud/storage工作。仅当您采取以下两种方式之一时才会这样做:

  1. 使用特殊服务帐户来初始化SDK,通常是通过第二个非默认实例。
  2. 或者,通过将"signBlob"权限授予默认的App Engine服务帐户,而不使用服务帐户。
0