简体   繁体   English

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

[英]Get Download URL from file uploaded with Cloud Functions for Firebase

After uploading a file in Firebase Storage with Functions for Firebase, I'd like to get the download url of the file.在 Firebase Storage with Functions for Firebase 上传文件后,我想下载 url 的文件。

I have this:我有这个:

...

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

        // Get the download url of file

    });

The object file has a lot of parameters. object这个文件有很多参数。 Even one named mediaLink .甚至一个名为mediaLink However, if I try to access this link, I get this error:但是,如果我尝试访问此链接,则会出现此错误:

Anonymous users does not have storage.objects.get access to object...匿名用户没有 storage.objects.get 访问 object...

Can somebody tell me how to get the public download Url?有人可以告诉我如何获得公共下载 Url 吗?

Thank you谢谢

You'll need to generate a signed URL using getSignedURL via the @google-cloud/storage NPM module.您需要通过@google-cloud/storage NPM 模块使用getSignedURL生成签名 URL。

Example:例子:

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

You'll need to initialize @google-cloud/storage with your service account credentials as the application default credentials will not be sufficient.您需要使用您的服务帐户凭据初始化@google-cloud/storage ,因为应用程序默认凭据是不够的。

UPDATE : The Cloud Storage SDK can now be accessed via the Firebase Admin SDK, which acts as a wrapper around @google-cloud/storage.更新:现在可以通过 Firebase Admin SDK 访问 Cloud Storage SDK,它充当@google-cloud/storage的包装器 The only way it will is if you either:唯一的方法是,如果您:

  1. Init the SDK with a special service account, typically through a second, non-default instance.使用特殊服务帐户初始化 SDK,通常是通过第二个非默认实例。
  2. Or, without a service account, by giving the default App Engine service account the "signBlob" permission.或者,在没有服务帐户的情况下,通过向默认 App Engine 服务帐户授予“signBlob”权限。

Here's an example on how to specify the download token on upload:以下是有关如何在上传时指定下载令牌的示例:

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

then call with然后打电话给

upload(localPath, remotePath).then( downloadURL => {
    console.log(downloadURL);
  });

The key thing here is that there is a metadata object nested within the metadata option property.这里的关键是metadata metadata选项属性中嵌套了一个metadata对象。 Setting firebaseStorageDownloadTokens to a uuid-v4 value will tell Cloud Storage to use that as its public auth token.firebaseStorageDownloadTokens设置为 uuid-v4 值将告诉 Cloud Storage 将其用作其公共身份验证令牌。

Many thanks to @martemorfosis非常感谢@martemorfosis

This answer will summarize the options for getting the download URL when uploading a file to Google/Firebase Cloud Storage.此答案将总结在将文件上传到 Google/Firebase 云存储时获取下载 URL 的选项。 There are three types of download URLS:下载 URL 分为三种类型:

  1. signed download URLs, which are temporary and have security features已签名的下载 URL,它们是临时的且具有安全功能
  2. token download URLs, which are persistent and have security features令牌下载 URL,它们是持久的并且具有安全功能
  3. public download URLs, which are persistent and lack security公共下载 URL,持久且缺乏安全性

There are three ways to get a token download URL.可以通过三种方式获取令牌下载 URL。 The other two download URLs have only one way to get them.其他两个下载 URL 只有一种获取方式。

From the Firebase Storage Console从 Firebase 存储控制台

You can get the download URL from Firebase Storage console:您可以从 Firebase Storage 控制台获取下载 URL:

在此处输入图片说明

The download URL looks like this:下载 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

The first part is a standard path to your file.第一部分是文件的标准路径。 At the end is the token.最后是令牌。 This download URL is permanent, ie, it won't expire, although you can revoke it.此下载 URL 是永久性的,即它不会过期,但您可以撤销它。

getDownloadURL() From the Front End getDownloadURL() 从前端

The documentation tells us to use getDownloadURL() : 文档告诉我们使用getDownloadURL()

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

This gets the same download URL that you can get from your Firebase Storage console.这与您可以从 Firebase Storage 控制台获得的下载 URL 相同。 This method is easy but requires that you know the path to your file, which in my app is about 300 lines of code, for a relatively simple database structure.这种方法很简单,但要求您知道文件的路径,在我的应用程序中,对于相对简单的数据库结构,大约有 300 行代码。 If your database is complex this would be a nightmare.如果您的数据库很复杂,这将是一场噩梦。 And you could upload files from the front end, but this would expose your credentials to anyone who downloads your app.您可以从前端上传文件,但这会将您的凭据暴露给下载您的应用程序的任何人。 So for most projects you'll want to upload your files from your Node back end or Google Cloud Functions, then get the download URL and save it to your database along with other data about your file.因此,对于大多数项目,您需要从 Node 后端或 Google Cloud Functions 上传文件,然后获取下载 URL 并将其与有关文件的其他数据一起保存到数据库中。

getSignedUrl() for Temporary Download URLs getSignedUrl() 用于临时下载 URL

getSignedUrl() is easy to use from a Node back end or Google Cloud Functions: getSignedUrl()很容易从 Node 后端或 Google Cloud Functions 使用:

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

A signed download URL looks like this:签名的下载 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

The signed URL has an expiration date and long signature.签名 URL 具有到期日期和长签名。 The documentation for the command line gsutil signurl -d says that signed URLs are temporary: the default expiration is one hour and the maximum expiration is seven days.命令行gsutil signurl -d的文档说签名 URL 是临时的:默认过期时间为一小时,最长过期时间为 7 天。

I'm going to rant here that getSignedUrl never says that your signed URL will expire in a week.我要在这里咆哮getSignedUrl从来没有说你的签名 URL 会在一周内过期。 The documentation code has 3-17-2025 as the expiration date, suggesting that you can set the expiration years in the future.文档代码有3-17-2025为到期日,建议您可以设置未来的到期年。 My app worked perfectly, and then crashed a week later.我的应用程序运行良好,然后在一周后崩溃了。 The error message said that the signatures didn't match, not that the download URL had expired.错误消息表示签名不匹配,而不是下载 URL 已过期。 I made various changes to my code, and everything worked...until it all crashed a week later.我对我的代码进行了各种更改,一切正常……直到一周后它都崩溃了。 This went on for more than a month of frustration.这持续了一个多月的挫败感。

Make Your File Publicly Available公开您的文件

You can set the permissions on your file to public read, as explained in the documentation .您可以将文件的权限设置为公开读取,如文档中所述。 This can be done from the Cloud Storage Browser or from your Node server.这可以通过云存储浏览器或您的节点服务器完成。 You can make one file public or a directory or your entire Storage database.您可以公开一个文件或一个目录或整个 Storage 数据库。 Here's the Node code:这是节点代码:

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

The result will look like this in your Cloud Storage Browser:结果在您的云存储浏览器中将如下所示:

在此处输入图片说明

Anyone can then use the standard path to download your file:然后任何人都可以使用标准路径下载您的文件:

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

Another way to make a file public is to use the method makePublic() .另一种公开文件的方法是使用makePublic()方法。 I haven't been able to get this to work, it's tricky to get the bucket and file paths right.我一直无法让它工作,让存储桶和文件路径正确是很棘手的。

An interesting alternative is to use Access Control Lists .一个有趣的替代方法是使用访问控制列表 You can make a file available only to users whom you put on a list, or use authenticatedRead to make the file available to anyone who is logged in from a Google account.您可以将文件仅提供给您列入列表的用户使用,或者使用authenticatedRead将文件提供给从 Google 帐户登录的任何人。 If there were an option "anyone who logged into my app using Firebase Auth" I would use this, as it would limit access to only my users.如果有一个选项“任何使用 Firebase Auth 登录我的应用程序的人”,我都会使用它,因为它会限制只有我的用户才能访问。

Build Your Own Download URL with firebaseStorageDownloadTokens使用 firebaseStorageDownloadTokens 构建您自己的下载 URL

Several answers describe an undocumented Google Storage object property firebaseStorageDownloadTokens .几个答案描述了一个未记录的 Google Storage 对象属性firebaseStorageDownloadTokens With this you can tell Storage the token you want to use.有了这个,您可以告诉 Storage 您要使用的令牌。 You can generate a token with the uuid Node module.您可以使用uuid节点模块生成令牌。 Four lines of code and you can build your own download URL, the same download URL you get from the console or getDownloadURL() .四行代码,您可以构建自己的下载 URL,与您从控制台或getDownloadURL()获得的下载 URL 相同。 The four lines of code are:这四行代码是:

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

Here's the code in context:这是上下文中的代码:

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

That's not a typo--you have to nest firebaseStorageDownloadTokens in double layers of metadata: !这不是打字错误——您必须将firebaseStorageDownloadTokens嵌套在双层metadata:

Doug Stevenson pointed out that firebaseStorageDownloadTokens is not an official Google Cloud Storage feature. Doug Stevenson 指出firebaseStorageDownloadTokens不是谷歌云存储的官方功能。 You won't find it in any Google documentation, and there's no promise it will be in future version of @google-cloud .您不会在任何 Google 文档中找到它,也不保证它会出现在@google-cloud未来版本中。 I like firebaseStorageDownloadTokens because it's the only way to get what I want, but it has a "smell" that it's not safe to use.我喜欢firebaseStorageDownloadTokens因为它是获得我想要的东西的唯一方法,但它有一种“气味”,使用起来并不安全。

Why No getDownloadURL() from Node?为什么没有来自 Node 的 getDownloadURL()?

As @Clinton wrote, Google should make a file.getDownloadURL() a method in @google-cloud/storage (ie, your Node back end).正如@Clinton 所写,Google 应该在@google-cloud/storage (即您的 Node 后端)中创建一个file.getDownloadURL()方法。 I want to upload a file from Google Cloud Functions and get the token download URL.我想从 Google Cloud Functions 上传文件并获取令牌下载 URL。

If you're working on a Firebase project, you can create signed URLs in a Cloud Function without including other libraries or downloading a credentials file.如果您正在处理 Firebase 项目,则可以在 Cloud Function 中创建签名网址,而无需包含其他库或下载凭据文件。 You just need to enable the IAM API and add a role to your existing service account (see below).您只需要启用 IAM API 并向您现有的服务账户添加一个角色(见下文)。

Initialize the admin library and get a file reference as your normally would:像往常一样初始化管理库并获取文件引用:

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')

You then generate a signed URL with然后您生成一个签名的 URL

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

Make sure your Firebase service account has sufficient permissions to run this确保您的 Firebase 服务帐户有足够的权限来运行此

  1. Go to the Google API console and enable the IAM API ( https://console.developers.google.com/apis/api/iam.googleapis.com/overview )转到 Google API 控制台并启用 IAM API ( https://console.developers.google.com/apis/api/iam.googleapis.com/overview )
  2. Still in the API console, go to the main menu, "IAM & admin" -> "IAM"还是在API控制台,进入主菜单,“IAM & admin”->“IAM”
  3. Click edit for the "App Engine default service account" role单击“App Engine 默认服务帐户”角色的编辑
  4. Click "Add another role", and add the one called "Service Account Token Creator"点击“添加另一个角色”,添加名为“Service Account Token Creator”的角色
  5. Save and wait a minute for the changes to propagate保存并等待更改传播

With a vanilla Firebase config, the first time you run the above code you'll get an error Identity and Access Management (IAM) API has not been used in project XXXXXX before or it is disabled.使用 vanilla Firebase 配置时,第一次运行上述代码时,您会收到错误身份和访问管理 (IAM) API 之前未在项目 XXXXXX 中使用或已禁用。 . . If you follow the link in the error message and enable the IAM API, you'll get another error: Permission iam.serviceAccounts.signBlob is required to perform this operation on service account my-service-account .如果您按照错误消息中的链接启用 IAM API,您将收到另一个错误: Permission iam.serviceAccounts.signBlob is required to perform this operation on service account my-service-account Adding the Token Creator role fixes this second permission issue.添加 Token Creator 角色修复了第二个权限问题。

You should avoid harcoding URL prefix in your code, especially when there are alternatives .您应该避免在代码中对 URL 前缀进行硬编码,尤其是在有替代方案时 I suggest using the option predefinedAcl: 'publicRead' when uploading a file with Cloud Storage NodeJS 1.6.x or +:我建议在使用Cloud Storage NodeJS 1.6.x或 + 上传文件时使用选项predefinedAcl: 'publicRead'

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

bucket.upload(attachment, options);

Then, getting the public URL is as simple as:然后,获取公共 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);
});

With the recent changes in the functions object response you can get everything you need to "stitch" together the download URL like so:随着函数对象响应的最新变化,您可以获得“拼接”下载 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);

One method I'm using with success is to set a UUID v4 value to a key named firebaseStorageDownloadTokens in the metadata of the file after it finishes uploading and then assemble the download URL myself following the structure Firebase uses to generate these URLs, eg:我成功使用的一种方法是在文件上传完成后,将 UUID v4 值设置为文件元数据中名为firebaseStorageDownloadTokens的键,然后按照 Firebase 用于生成这些 URL 的结构自己组装下载 URL,例如:

https://firebasestorage.googleapis.com/v0/b/[BUCKET_NAME]/o/[FILE_PATH]?alt=media&token=[THE_TOKEN_YOU_CREATED]

I don't know how much "safe" is to use this method (given that Firebase could change how it generates the download URLs in the future ) but it is easy to implement.我不知道使用这种方法有多“安全”(假设 Firebase 将来可能会改变它生成下载 URL 的方式),但它很容易实现。

For those wondering where the Firebase Admin SDK serviceAccountKey.json file should go.对于那些想知道 Firebase Admin SDK serviceAccountKey.json 文件应该放在何处的人。 Just place it in the functions folder and deploy as usual.只需将它放在函数文件夹中并像往常一样部署。

It still baffles me why we can't just get the download url from the metadata like we do in the Javascript SDK.它仍然让我感到困惑,为什么我们不能像在 Javascript SDK 中那样从元数据中获取下载 url。 Generating a url that will eventually expire and saving it in the database is not desirable.生成一个最终会过期的 url 并将其保存在数据库中是不可取的。

This is what I currently use, it's simple and it works flawlessly.这就是我目前使用的,它很简单,而且运行完美。

You don't need to do anything with Google Cloud.您无需对 Google Cloud 执行任何操作。 It works out of the box with Firebase..它与 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

EDIT: Same example, but with upload:编辑:相同的例子,但上传:

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

#update: no need to make two different call in upload method to get the metadata: #update:无需在上传方法中进行两次不同的调用来获取元数据:

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

Sorry but i can't post a comment to your question above because of missing reputation, so I will include it in this answer.抱歉,由于缺少声誉,我无法对上述问题发表评论,因此我会将其包含在此答案中。

Do as stated above by generating a signed Url, but instead of using the service-account.json I think you have to use the serviceAccountKey.json which you can generate at (replace YOURPROJECTID accordingly)如上所述通过生成一个签名的 Url,但不是使用 service-account.json 我认为你必须使用你可以生成的 serviceAccountKey.json (相应地替换 YOURPROJECTID)

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

Example:例子:

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

I can't comment on the answer James Daniels gave, but I think this is very Important to read.我无法对 James Daniels 给出的答案发表评论,但我认为这是非常重要的阅读。

Giving out a signed URL Like he did seems for many cases pretty bad and possible Dangerous .像他那样给出一个签名的 URL 在许多情况下似乎很糟糕,可能是危险的 According to the documentation of Firebase the signed url expires after some time, so adding that to your databse will lead to a empty url after a certain timeframe根据 Firebase 的文档,签名的 url 会在一段时间后过期,因此将其添加到您的数据库将在特定时间范围后导致空 url

It may be that misunderstood the Documentation there and the signed url doesn't expire, which would have some security issues as a result.可能是误解了那里的文档并且签名的 url 没有过期,这会导致一些安全问题。 The Key seems to be the same for every uploaded file.每个上传的文件的密钥似乎都相同。 This means once you got the url of one file, someone could easily access files that he is not suposed to access, just by knowing their names.这意味着一旦您获得了一个文件的 url,某人就可以轻松访问他不应该访问的文件,只需知道他们的名字。

If i missunderstood that then i would lvoe to be corrected.如果我误解了,那么我会得到纠正。 Else someone should probably Update the above named solution.其他人可能应该更新上面命名的解决方案。 If i may be wrong there如果我可能错了

If you use the predefined access control lists value of 'publicRead', you can upload the file and access it with a very simple url structure:如果您使用“publicRead”的预定义访问控制列表值,您可以上传文件并使用非常简单的 url 结构访问它:

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

You can then construct the url like so:然后,您可以像这样构造 url:

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

I had the same issue, however, I was looking at the code of the firebase function example instead of the README.我遇到了同样的问题,但是,我正在查看 firebase 函数示例的代码而不是自述文件。 And Answers on this thread didn't help either...这个线程上的答案也没有帮助......

You can avoid passing the config file by doing the following:您可以通过执行以下操作来避免传递配置文件:

Go to your project's Cloud Console > IAM & admin > IAM , Find the App Engine default service account and add the Service Account Token Creator role to that member.转到您项目的Cloud Console > IAM & admin > IAM ,找到 App Engine 默认服务帐户并将服务帐户令牌创建者角色添加到该成员。 This will allow your app to create signed public URLs to the images.这将允许您的应用程序创建图像的签名公共 URL。

source: Automatically Generate Thumbnails function README来源: 自动生成缩略图功能自述文件

Your role for app engine should look like this:您在应用引擎中的角色应如下所示:

云控制台

This works if you just need a public file with a simple URL.如果您只需要一个带有简单 URL 的公共文件,这将起作用。 Note that this may overrule your Firebase storage rules.请注意,这可能会否决您的 Firebase 存储规则。

bucket.upload(file, function(err, file) {
    if (!err) {
      //Make the file public
      file.acl.add({
      entity: 'allUsers',
      role: gcs.acl.READER_ROLE
      }, function(err, aclObject) {
          if (!err) {
              var URL = "https://storage.googleapis.com/[your bucket name]/" + file.id;
              console.log(URL);
          } else {
              console.log("Failed to set permissions: " + err);
          }
      });  
    } else {
        console.log("Upload failed: " + err);
    }
});

Without signedURL() using makePublic()如果没有signedURL()使用makePublic()

const functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp()
var bucket = admin.storage().bucket();

// --- [Above] for admin related operations, [Below] for making a public url from a GCS uploaded object

const { Storage } = require('@google-cloud/storage');
const storage = new Storage();

exports.testDlUrl = functions.storage.object().onFinalize(async (objMetadata) => {
    console.log('bucket, file', objMetadata.bucket + ' ' + objMetadata.name.split('/').pop()); // assuming file is in folder
    return storage.bucket(objMetadata.bucket).file(objMetadata.name).makePublic().then(function (data) {
        return admin.firestore().collection('publicUrl').doc().set({ publicUrl: 'https://storage.googleapis.com/' + objMetadata.bucket + '/' + objMetadata.name }).then(writeResult => {
            return console.log('publicUrl', writeResult);
        });
    });
});

answer by https://stackoverflow.com/users/269447/laurent works best https://stackoverflow.com/users/269447/laurent 的回答效果最好

const uploadOptions: UploadOptions = {
    public: true
};

const bucket = admin.storage().bucket();
[ffile] = await bucket.upload(oPath, uploadOptions);
ffile.metadata.mediaLink // this is what you need

For those who are using Firebase SDK and admin.initializeApp :对于那些使用 Firebase SDK 和admin.initializeApp

1 - Generate a Private Key and place in /functions folder. 1 - 生成私钥并放在 /functions 文件夹中。

2 - Configure your code as follows: 2 - 按如下方式配置您的代码:

const serviceAccount = require('../../serviceAccountKey.json');
try { admin.initializeApp(Object.assign(functions.config().firebase, { credential: admin.credential.cert(serviceAccount) })); } catch (e) {}

Documentation文档

The try/catch is because I'm using a index.js that imports other files and creates one function to each file. try/catch 是因为我使用的 index.js 导入其他文件并为每个文件创建一个函数。 If you're using a single index.js file with all functions, you should be ok with admin.initializeApp(Object.assign(functions.config().firebase, { credential: admin.credential.cert(serviceAccount) }));如果您使用具有所有功能的单个 index.js 文件,您应该可以使用admin.initializeApp(Object.assign(functions.config().firebase, { credential: admin.credential.cert(serviceAccount) })); . .

As of firebase 6.0.0 I was able to access the storage directly with the admin like this:从 firebase 6.0.0 开始,我可以像这样直接通过管理员访问存储:

const bucket = admin.storage().bucket();

So I didn't need to add a service account.所以我不需要添加服务帐户。 Then setting the UUID as referenced above worked for getting the firebase url.然后设置上面提到的 UUID 用于获取 firebase url。

This is the best I came up.这是我想到的最好的。 It is redundant, but the only reasonable solution that worked for me.这是多余的,但对我有用的唯一合理的解决方案。

await bucket.upload(localFilePath, {destination: uploadPath, public: true});
const f = await bucket.file(uploadPath)
const meta = await f.getMetadata()
console.log(meta[0].mediaLink)

I already post my ans... in below URL Where you can get full code with solution我已经在下面的 URL 中发布了我的 ans... 在那里您可以获得带有解决方案的完整代码

How do I upload a base64 encoded image (string) directly to a Google Cloud Storage bucket using Node.js? 如何使用 Node.js 将 base64 编码的图像(字符串)直接上传到 Google Cloud Storage 存储桶?

const uuidv4 = require('uuid/v4');
const uuid = uuidv4();

    const os = require('os')
    const path = require('path')
    const cors = require('cors')({ origin: true })
    const Busboy = require('busboy')
    const fs = require('fs')
    var admin = require("firebase-admin");


    var serviceAccount = {
        "type": "service_account",
        "project_id": "xxxxxx",
        "private_key_id": "xxxxxx",
        "private_key": "-----BEGIN PRIVATE KEY-----\jr5x+4AvctKLonBafg\nElTg3Cj7pAEbUfIO9I44zZ8=\n-----END PRIVATE KEY-----\n",
        "client_email": "xxxx@xxxx.iam.gserviceaccount.com",
        "client_id": "xxxxxxxx",
        "auth_uri": "https://accounts.google.com/o/oauth2/auth",
        "token_uri": "https://oauth2.googleapis.com/token",
        "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
        "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-5rmdm%40xxxxx.iam.gserviceaccount.com"
      }

    admin.initializeApp({
        credential: admin.credential.cert(serviceAccount),
        storageBucket: "xxxxx-xxxx" // use your storage bucket name
    });


    const app = express();
    app.use(bodyParser.urlencoded({ extended: false }));
    app.use(bodyParser.json());
app.post('/uploadFile', (req, response) => {
    response.set('Access-Control-Allow-Origin', '*');
    const busboy = new Busboy({ headers: req.headers })
    let uploadData = null
    busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
        const filepath = path.join(os.tmpdir(), filename)
        uploadData = { file: filepath, type: mimetype }
        console.log("-------------->>",filepath)
        file.pipe(fs.createWriteStream(filepath))
      })

      busboy.on('finish', () => {
        const bucket = admin.storage().bucket();
        bucket.upload(uploadData.file, {
            uploadType: 'media',
            metadata: {
              metadata: { firebaseStorageDownloadTokens: uuid,
                contentType: uploadData.type,
              },
            },
          })

          .catch(err => {
            res.status(500).json({
              error: err,
            })
          })
      })
      busboy.end(req.rawBody)
   });




exports.widgets = functions.https.onRequest(app);

For those trying to use the token parameter to share the file and would like to use gsutil command, here is how I did it:对于那些试图使用 token 参数来共享文件并想使用 gsutil 命令的人,我是这样做的:

First you need to authenticate by running: gcloud auth首先,您需要通过运行进行身份验证: gcloud auth

Then run:然后运行:

gsutil setmeta -h "x-goog-meta-firebaseStorageDownloadTokens:$FILE_TOKEN" gs://$FIREBASE_REPO/$FILE_NAME gsutil setmeta -h "x-goog-meta-firebaseStorageDownloadTokens:$FILE_TOKEN" gs://$FIREBASE_REPO/$FILE_NAME

Then you can download the file with the following link:然后,您可以使用以下链接下载文件:

https://firebasestorage.googleapis.com/v0/b/$FIREBASE_REPO/o/$FILE_NAME?alt=media&token=$FILE_TOKEN https://firebasestorage.googleapis.com/v0/b/$FIREBASE_REPO/o/$FILE_NAME?alt=media&token=$FILE_TOKEN

Use file.publicUrl()使用file.publicUrl()

Async/Await异步/等待

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

Callback打回来

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

  const downloadUrl = file.publicUrl();
})

The downloadUrl will be "https://storage.googleapis.com/bucket-name/image-name.jpg" . downloadUrl将为"https://storage.googleapis.com/bucket-name/image-name.jpg"

Please note that in order for the above code to work, you have to make the bucket or file public.请注意,为了使上述代码工作,您必须公开存储桶或文件。 To do so, follow the instructions here https://cloud.google.com/storage/docs/access-control/making-data-public为此,请按照此处的说明进行操作https://cloud.google.com/storage/docs/access-control/making-data-public

From the Admin SDKs, you cannot retrieve the download token generated by Firebase of an uploaded file, but you can set that token when uploading by adding it in the metadata.从 Admin SDK 中,您无法检索上传文件的 Firebase 生成的下载令牌,但您可以在上传时通过将其添加到元数据中来设置该令牌。

For those who are working on Python SDK. This is the way to do it:对于那些在 Python SDK 上工作的人。这是这样做的方法:

from firebase_admin import storage
from uuid import uuid4

bucket = storage.bucket()
blob = bucket.blob(path_to_file)
token = str(uuid4()) # Random ID

blob.metadata = {
        "firebaseStorageDownloadTokens": token
    }
blob.upload_from_file(file)

You have now uploaded the file and got the URL token.您现在已经上传了文件并获得了 URL 令牌。 You could now save the token (or even the full download URL) into your database (eg Firestore) and send it to the client when the file is requested and then making the client itself retrieve the file.您现在可以将令牌(甚至完整的下载 URL)保存到您的数据库(例如 Firestore)中,并在请求文件时将其发送给客户端,然后让客户端自己检索文件。

The full download URL looks like this:完整下载 URL 如下所示:

https://firebasestorage.googleapis.com/v0/b/{bucket_name}/o/{file_name}?alt=media&token={token}

If you are getting error:如果您收到错误:

Google Cloud Functions: require(…) is not a function谷歌云函数:require(…) 不是函数

try this:尝试这个:

const {Storage} = require('@google-cloud/storage');
const storage = new Storage({keyFilename: 'service-account-key.json'});
const bucket = storage.bucket(object.bucket);
const file = bucket.file(filePath);
.....

I saw this on the admin storage doc我在管理存储文档上看到了这个

const options = {
  version: 'v4',
  action: 'read',
  expires: Date.now() + 15 * 60 * 1000, // 15 minutes
};

// Get a v4 signed URL for reading the file
const [url] = await storage
  .bucket(bucketName)
  .file(filename)
  .getSignedUrl(options);

console.log('Generated GET signed URL:');
console.log(url);
console.log('You can use this URL with any user agent, for example:');
console.log(`curl '${url}'`);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用下载 url 和 Cloud Functions 从 Firebase 存储中删除文件 - Delete a file from firebase storage using download url with Cloud Functions 上传到Firebase存储时如何使用Cloud Functions for FIrebase获取mp3文件的持续时间 - How to get the duration of a mp3 file using Cloud Functions for FIrebase when uploaded to firebase storage Cloud Funtions从创建的文件获取下载网址 - Cloud Funtions Get download Url from created file Firebase云功能:如何从FTP帐户检索文件并将其下载到浏览器中 - Firebase cloud functions: how to retrieve a file from an FTP account and download it in the browser Firebase Cloud function-Get document snapshot field which is the URL of a file in firebase storage and delete the file from firebase storage - js - Firebase Cloud function-Get document snapshot field which is the URL of a file in firebase storage and delete the file from firebase storage - js 使用 firebase 函数重定向到 URL - 谷歌云函数 - Redirecting to URL with firebase functions - google cloud functions Firebase的云功能图像下载功能错误 - Cloud Functions for Firebase Image download function Error Firebase Cloud Functions-如何获取上载文件的用户? - Firebase Cloud Functions - How to get user that uploads a file? 使用云功能创建pdf文件并将其上传到firebase存储? (可以上传本地文件,但不能从云功能上传) - Create and upload pdf file to firebase storage using cloud functions? (can upload locally file, but not from cloud functions) 使用http cloud云功能从Firebase云存储下载文件时,没有此类文件或目录 - no such file or directory when downloading a file from firebase cloud storage using http cloud cloud functions
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM