从使用Cloud Functions for Firebase上传的文件中获取下载URL

Val*_*tin 86 node.js firebase google-cloud-functions firebase-storage

在Firebase存储中使用函数for Firebase上传文件后,我想获取该文件的下载URL.

我有这个 :

...

return bucket
    .upload(fromFilePath, {destination: toFilePath})
    .then((err, file) => {

        // Get the download url of file

    });
Run Code Online (Sandbox Code Playgroud)

目标文件有很多参数.甚至一个名字mediaLink.但是,如果我尝试访问此链接,我会收到此错误:

匿名用户没有storage.objects.get访问对象...

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

谢谢

Jam*_*els 109

您需要通过@ google-cloud/storage NPM模块使用getSignedURL生成签名网址.

例:

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
});
Run Code Online (Sandbox Code Playgroud)

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

更新:现在可以通过Firebase Admin SDK访问Cloud Storage SDK,它充当 @ google-cloud/storage 的包装器.唯一的方法是你要么:

  1. 使用特殊服务帐户启动SDK,通常通过第二个非默认实例.
  2. 或者,如果没有服务帐户,则为默认的App Engine服务帐户提供"signBlob"权限.

  • 这很奇怪.使用Firebase Android,iOS和Web SDK时,我们可以轻松地从存储参考中获取下载URL.为什么在管理员工作时不那么容易?PS:我在哪里可以找到初始化gcs所需的'service-account.json'? (52认同)
  • 使用此方法生成的URL非常长.由@martemorfosis提出的URL提出的方法要好得多.是否有任何产生该URL的函数?当我使用firebase-sdk时,这就是我在数据库中保存以备将来使用的内容.函数域中需要存在镜像方法. (13认同)
  • 谨防!签名 URL 不适合长时间使用,最多 2 周后过期 (v4)。如果您打算将其长期存储在数据库中,则签名 URL 不是正确的方法。在此处检查属性“expires”:https://googleapis.dev/nodejs/storage/latest/global.html#GetBucketSignedUrlConfig (5认同)
  • 这个网址会在一周后过期吗?还是有效期到 03-09-2491?@弗兰克范普弗伦 (3认同)
  • 不行,我的意思是只有7天,有没有一种简单的方法可以获取下载的URL而不会过期? (3认同)
  • 这是因为admin-sdk没有添加任何云存储.您可以在此处获取您的admin-sdk服务帐户json https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk (2认同)
  • 我们如何在部署的功能上上传service-account.json?我已经尝试将它放在函数文件夹中,并在package.json的文件字段中引用它,但它没有部署.谢谢. (2认同)
  • 我们是否需要添加`action`和`expires`? (2认同)
  • 更新的答案需要更多说明 (2认同)
  • 此解决方案返回一个疯狂的长网址,该网址与您进入 firebase 控制台并单击“获取下载网址”时获得的内容不匹配。它也不起作用 (2认同)

Dre*_*pre 62

以下是有关如何在上传时指定下载令牌的示例:

const UUID = require("uuid-v4");

const fbId = "<YOUR APP ID>";
const fbKeyFile = "./YOUR_AUTH_FIlE.json";
const gcs = require('@google-cloud/storage')({keyFilename: fbKeyFile});
const bucket = gcs.bucket(`${fbId}.appspot.com`);

var upload = (localFile, remoteFile) => {

  let uuid = UUID();

  return bucket.upload(localFile, {
        destination: remoteFile,
        uploadType: "media",
        metadata: {
          contentType: 'image/png',
          metadata: {
            firebaseStorageDownloadTokens: uuid
          }
        }
      })
      .then((data) => {

          let file = data[0];

          return Promise.resolve("https://firebasestorage.googleapis.com/v0/b/" + bucket.name + "/o/" + encodeURIComponent(file.name) + "?alt=media&token=" + uuid);
      });
}
Run Code Online (Sandbox Code Playgroud)

然后打电话给

upload(localPath, remotePath).then( downloadURL => {
    console.log(downloadURL);
  });
Run Code Online (Sandbox Code Playgroud)

这里的关键是metadatametadataoption属性中嵌套了一个对象.设置firebaseStorageDownloadTokens为uuid-v4值将告知云存储将其用作其公共身份验证令牌.

非常感谢@martemorfosis

  • 在@martemorfosis帖子中找到了答案.可以从object.metadata exports.uploadProfilePic = functions.storage.object().onChange中检索UUID(event => {const object = event.data; //存储对象.const uuid = object.metadata.firebaseStorageDownloadTokens; // ... (3认同)
  • 这应该是正确的答案。它产生的 URL 类似于 Firebase SDK (@DevMike) 生成的 URL,我敢打赌这正是他们在幕后所做的事情。 (2认同)

Tho*_*hoe 22

此答案将总结在将文件上传到Google / Firebase Cloud Storage时获取下载URL的选项。共有三种下载URL:

  1. 签名的下载URL,这些URL是临时的并具有安全功能
  2. 令牌下载URL,这些URL具有永久性并具有安全功能
  3. 公共下载URL,这些URL持久且缺乏安全性

有三种获取令牌下载URL的方法。其他两个下载URL只有一种获取方式。

从Firebase存储控制台

您可以从Firebase存储控制台获取下载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
Run Code Online (Sandbox Code Playgroud)

第一部分是文件的标准路径。最后是令牌。此下载URL是永久性的,即即使您可以撤消它也不会过期。

从前端getDownloadURL()

文档告诉我们使用getDownloadURL()

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
Run Code Online (Sandbox Code Playgroud)

这将获得与您从Firebase Storage控制台获得的下载URL相同的下载URL。这种方法很简单,但是要求您知道文件的路径,对于我的应用程序,它的路径约为300行,以实现相对简单的数据库结构。如果您的数据库很复杂,那将是一场噩梦。您可以从前端上传文件,但这会将您的凭据公开给下载您的应用程序的任何人。因此,对于大多数项目,您需要从Node后端或Google Cloud Functions上传文件,然后获取下载URL并将其与有关文件的其他数据一起保存到数据库中。

临时下载URL的getSignedUrl()

从节点后端或Google Cloud Functions易于使用getSignedUrl()

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

签名的下载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
Run Code Online (Sandbox Code Playgroud)

签名的URL具有有效日期和长签名。命令行gsutil signurl -d的文档指出,已签名的URL是临时的:默认有效期为一小时,最大有效期为七天。

在这里,我要保证getSignedUrl永远不会说您签名的URL将在一周内过期。文档代码3-17-2025以到期日期为准,建议您设置将来的到期年份。我的应用程序运行正常,一周后崩溃了。错误消息指出签名不匹配,不是下载URL过期。我对代码进行了各种更改,一切正常……直到一周后所有崩溃。持续了一个多月的挫败感。

使文件公开可用

您可以将文件的权限设置为公开读取,如文档所述。可以从Cloud Storage Browser或您的Node服务器完成。您可以将一个文件或目录或整个存储数据库公开。这是节点代码:

  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);
            }
          });
        });
      });
    });
  }
Run Code Online (Sandbox Code Playgroud)

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

在此处输入图片说明

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

https://storage.googleapis.com/languagetwo-cd94d.appspot.com/Audio/English/United_States-OED-0/system.mp3
Run Code Online (Sandbox Code Playgroud)

公开文件的另一种方法是使用方法makePublic()。我一直无法使它正常工作,正确设置存储桶和文件路径非常棘手。

一个有趣的替代方法是使用访问控制列表。您可以使文件仅对您列出的用户可用,或用于authenticatedRead使文件对从Google帐户登录的任何人可用。如果有一个选项“使用Firebase Auth登录到我的应用程序的任何人”,我将使用此选项,因为它将限制仅对我的用户的访问。

使用firebaseStorageDownloadTokens构建自己的下载URL

有几个答案描述了一个未记录的Google Storage对象属性firebaseStorageDownloadTokens。这样,您可以告诉Storage您想要使用的令牌。您可以使用uuidNode模块生成令牌。四行代码,您可以构建自己的下载URL,即从控制台或获得的下载URL getDownloadURL()。四行代码是:

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
Run Code Online (Sandbox Code Playgroud)
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));
    });
Run Code Online (Sandbox Code Playgroud)
https://storage.googleapis.com/languagetwo-cd94d.appspot.com/Audio/English/United_States-OED-0/system.mp3
Run Code Online (Sandbox Code Playgroud)

这是上下文中的代码:

const uuidv4 = require('uuid/v4');
const uuid = uuidv4();
Run Code Online (Sandbox Code Playgroud)

这不是打字错误-您必须嵌套firebaseStorageDownloadTokensmetadata:

道格·史蒂文森(Doug Stevenson)指出,这firebaseStorageDownloadTokens不是Google Cloud Storage的正式功能。您不会在任何Google文档中找到它,也无法保证它将在的未来版本中使用@google-cloud。我喜欢,firebaseStorageDownloadTokens因为这是获得我想要的东西的唯一方法,但是它有“气味”,使用起来不安全。

为什么不从Node获得getDownloadURL()?

正如@Clinton所写,Google应该在其中创建file.getDownloadURL()一个方法@google-cloud/storage(即,您的Node后端)。我想从Google Cloud Functions上传文件并获取令牌下载URL。

  • 我为此在`@ google-cloud / storage`上创建了一个问题,随时对其进行+1;)https://github.com/googleapis/nodejs-storage/issues/697 (4认同)
  • 最新的 [makePublic()](https://googleapis.dev/nodejs/storage/latest/File.html#makePublic) 链接。 (2认同)
  • @thomas 感谢您的精彩总结!您提到有 3 种方法可以获取持久令牌下载 URL,但您只分享了 2 种:(a) 从 Firebase 存储控制台,以及 (b) 从前端 getDownloadURL()。我想知道第三种方法是什么? (2认同)
  • @czphilip如果你只想使用公共Url,你可以执行 `await admin.storage().bucket(object.bucket).file(object.name!).makePublic();` 然后 `object.mediaLink` 来制作该文件公开 (2认同)
  • getDownloadUrl 与公共对象相比有什么好处?难道每个人都不能访问每个链接吗?这是您可以撤销访问权限的唯一好处吗? (2认同)

SMX*_*SMX 20

如果您正在使用Firebase项目,则可以在云功能中创建签名URL,而无需包含其他库或下载凭据文件.您只需启用IAM API并将角色添加到现有服务帐户(请参阅下文).

初始化管理库并获得正常的文件引用:

import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'

admin.initializeApp(functions.config().firebase)

const myFile = admin.storage().bucket().file('path/to/my/file')
Run Code Online (Sandbox Code Playgroud)

然后,您使用生成签名的URL

myFile.getSignedUrl({action: 'read', expires: someDateObj}).then(urls => {
    const signedUrl = urls[0]
})
Run Code Online (Sandbox Code Playgroud)

确保您的Firebase服务帐户具有足够的权限来运行此帐户

  1. 转到Google API控制台并启用IAM API(https://console.developers.google.com/apis/api/iam.googleapis.com/overview)
  2. 仍然在API控制台中,转到主菜单"IAM&admin" - >"IAM"
  3. 单击"App Engine默认服务帐户"角色的编辑
  4. 单击"添加其他角色",然后添加名为"服务帐户令牌创建者"的角色
  5. 保存并等待一分钟以便传播更改

使用vanilla Firebase配置,第一次运行上述代码时,您将收到错误身份和访问管理(IAM)API尚未在项目XXXXXX中使用或被禁用..如果您按照错误消息中的链接并启用IAM API,您将收到另一个错误:需要权限iam.serviceAccounts.signBlob才能在服务帐户my-service-account上执行此操作.添加令牌创建者角色修复了第二个权限问题.

  • 我的签名网址将在 2 周后到期,但我使用 admin.initializeApp() 没有密钥,这是问题吗?我将 App Engine 应用默认服务帐户设置为“所有者”和 Cloud Functions 服务代理,我现在只是删除了“所有者”并添加了“服务帐户令牌创建者” (3认同)
  • 签名的URL将在7天后过期。您可以设置更短的到期日期,但不能更长。 (2认同)
  • 我收到错误“无法在没有 `client_email` 的情况下签署数据。” 使用模拟器 (2认同)

小智 17

随着函数对象响应的最近更改,您可以获得将下载URL"拼接"在一起所需的一切,如下所示:

 const img_url = 'https://firebasestorage.googleapis.com/v0/b/[YOUR BUCKET]/o/'
+ encodeURIComponent(object.name)
+ '?alt=media&token='
+ object.metadata.firebaseStorageDownloadTokens;

console.log('URL',img_url);
Run Code Online (Sandbox Code Playgroud)

  • 此解决方案的问题是服务URL是硬编码的.如果Firebase/Google更改了它,它可能会中断.使用`metadata.mediaLink`属性可以防止出现此类问题. (3认同)
  • 您是指来自`bucket.file()。upload()`的对象响应吗?我在响应数据中没有收到任何元数据属性,并且不确定如何获取这些“ firebaseStorageDownloadTokens”。 (2认同)
  • 建立这样的网址不受支持。它可能在今天工作,但将来可能会失败。您只应使用提供的API生成正确的下载URL。 (2认同)

Oli*_*xon 15

这就是我目前使用的,它很简单,而且运行完美。

您无需对 Google Cloud 执行任何操作。它与 Firebase 一起开箱即用。

// Save the base64 to storage.
const file = admin.storage().bucket('url found on the storage part of firebase').file(`profile_photos/${uid}`);
await file.save(base64Image, {
    metadata: {
      contentType: 'image/jpeg',
    },
    predefinedAcl: 'publicRead'
});
const metaData = await file.getMetadata()
const url = metaData[0].mediaLink
Run Code Online (Sandbox Code Playgroud)

编辑:相同的例子,但上传:

await bucket.upload(fromFilePath, {destination: toFilePath});
file = bucket.file(toFilePath);
metaData = await file.getMetadata()
const trimUrl = metatata[0].mediaLink
Run Code Online (Sandbox Code Playgroud)

#update:无需在上传方法中进行两次不同的调用来获取元数据:

let file = await bucket.upload(fromFilePath, {destination: toFilePath});
const trimUrl = file[0].metatata.mediaLink
Run Code Online (Sandbox Code Playgroud)

  • 它不是 mediaLinkenter,它只是 mediaLink (2认同)
  • @OliverDixon 这个方法有时间限制吗?我的意思是,上面接受的答案中的signedURL 仅在 7 天内有效。使用此 mediaLink 生成的 url 怎么样?超过7天? (2认同)

Cli*_*ton 14

对于那些想知道Firebase Admin SDK serviceAccountKey.json文件应该去哪里的人.只需将其放在函数文件夹中并照常部署即可.

它仍然让我感到困惑,为什么我们不能像在Javascript SDK中那样从元数据中获取下载URL.生成最终将过期并将其保存在数据库中的URL是不可取的.


Lau*_*ent 14

我建议在使用Cloud Storage NodeJS 1.6.x或+ predefinedAcl: 'publicRead'上传文件时使用该选项:

const options = {
    destination: yourFileDestination,
    predefinedAcl: 'publicRead'
};

bucket.upload(attachment, options);
Run Code Online (Sandbox Code Playgroud)

然后,获取公共URL非常简单:

bucket.upload(attachment, options).then(result => {
    const file = result[0];
    return file.getMetadata();
}).then(results => {
    const metadata = results[0];
    console.log('metadata=', metadata.mediaLink);
}).catch(error => {
    console.error(error);
});
Run Code Online (Sandbox Code Playgroud)


小智 13

我成功使用的一种方法是firebaseStorageDownloadTokens在完成上传后将UUID v4值设置为文件元数据中指定的键,然后按照Firebase用于生成这些URL的结构自行组装下载URL,例如:

https://firebasestorage.googleapis.com/v0/b/[BUCKET_NAME]/o/[FILE_PATH]?alt=media&token=[THE_TOKEN_YOU_CREATED]
Run Code Online (Sandbox Code Playgroud)

我不知道使用这种方法有多"安全"(考虑到Firebase可能会改变将来生成下载URL的方式),但它很容易实现.

  • 我不会认为这种技术是“安全的”,因为下载 URL 是不透明的,其组件不会被分解或组装。 (5认同)

NiV*_*K92 9

抱歉,由于缺少声誉,我无法在上面的问题上发表评论,所以我会将其包含在此答案中.

通过生成签名的Url,如上所述,但不是使用service-account.json我认为你必须使用你可以生成的serviceAccountKey.json(相应地替换YOURPROJECTID)

https://console.firebase.google.com/project/YOURPROJECTID/settings/serviceaccounts/adminsdk

例:

const gcs = require('@google-cloud/storage')({keyFilename: 'serviceAccountKey.json'});
// ...
const bucket = gcs.bucket(bucket);
// ...
return bucket.upload(tempLocalFile, {
        destination: filePath,
        metadata: {
          contentType: 'image/jpeg'
        }
      })
      .then((data) => {
        let file = data[0]
        file.getSignedUrl({
          action: 'read',
          expires: '03-17-2025'
        }, function(err, url) {
          if (err) {
            console.error(err);
            return;
          }

          // handle url 
        })
Run Code Online (Sandbox Code Playgroud)


小智 8

我无法评论James Daniels给出的答案,但是我认为这很重要。

像他那样提供一个签名的URL在许多情况下看起来都很糟糕,甚至有危险。根据Firebase的文档,签名的URL会在一段时间后过期,因此将其添加到您的数据库将导致在特定时间段后出现空URL

可能是误解了那里的文档,并且签名的url没有过期,因此会带来一些安全问题。每个上载文件的密钥似乎都相同。这意味着,一旦您获得一个文件的url,则只要知道文件名,某人就可以轻松访问他不应该访问的文件。

如果我误解了,那我会纠正。其他人可能应该更新上面命名的解决方案。如果我在那里错了


Jae*_*ung 8

使用file.publicUrl()

异步/等待

const bucket = storage.bucket('bucket-name');
const uploadResponse = await bucket.upload('image-name.jpg');
const downloadUrl = uploadResponse[0].publicUrl();
Run Code Online (Sandbox Code Playgroud)

打回来

const bucket = storage.bucket('bucket-name');
bucket.upload('image-name.jpg', (err, file) => {
  if(!file) {
    throw err;
  }

  const downloadUrl = file.publicUrl();
})
Run Code Online (Sandbox Code Playgroud)

将会downloadUrl"https://storage.googleapis.com/bucket-name/image-name.jpg"

请注意,为了使上述代码正常工作,您必须公开存储桶或文件。为此,请按照此处的说明进行操作https://cloud.google.com/storage/docs/access-control/making-data-public。另外,我@google-cloud/storage直接导入了包,而不是通过 Firebase SDK。


Fra*_*len 8

火力战士在这里

Node.js 的 Admin SDK版本 11.10getDownloadURL()添加了一个方法。有关如何使用它的示例可以在有关获取可共享 URL 的文档中找到:

const { getStorage, getDownloadURL } = require('firebase-admin/storage');

const fileRef = getStorage().bucket("my-bucket").file("my-file");
const downloadURL= await getDownloadURL(fileRef);
Run Code Online (Sandbox Code Playgroud)


ino*_*nik 6

如果您使用“publicRead”的预定义访问控制列表值,您可以上传文件并使用非常简单的 url 结构访问它:

// Upload to GCS
const opts: UploadOptions = {
  gzip: true,
  destination: dest, // 'someFolder/image.jpg'
  predefinedAcl: 'publicRead',
  public: true
};
return bucket.upload(imagePath, opts);
Run Code Online (Sandbox Code Playgroud)

然后,您可以像这样构造 url:

const storageRoot = 'https://storage.googleapis.com/';
const bucketName = 'myapp.appspot.com/'; // CHANGE TO YOUR BUCKET NAME
const downloadUrl = storageRoot + bucketName + encodeURIComponent(dest);
Run Code Online (Sandbox Code Playgroud)