如何使用从firestore触发的firebase云功能写入云存储桶? [英] How to write to a cloud storage bucket with a firebase cloud function triggered from firestore?

查看:31
本文介绍了如何使用从firestore触发的firebase云功能写入云存储桶?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了确保它尽可能干净和隔离,我创建了一个新的测试项目,其中除了这些步骤之外什么都没有.

To make sure this is as clean and isolated as possible, I created a new test project with nothing in it other than these steps.

然后我在控制台中启用了 Cloud Firestore.我创建了一个名为blarg"的新系列(好名字,嗯!).我向其中添加了一个文档,并使用了自动 ID,然后添加了一个名为humpty"的字段和字符串 dumpty.在控制台中是这样的:

I then enabled cloud firestore in the console. I created a new collection called "blarg" (great name, eh!). I added a document to it, and used the auto-id, then added a field named "humpty" with the string dumpty. It looks like this in the console:

我在 firebase 控制台的存储部分创建了一个名为 signatures 的目录.当我的函数(如下)触发时,我想在这个目录中写入一个文件.

I created a directory called signatures in the storage section of the firebase console. I want to write a file into this directory when my function (below) triggers.

然后我使用以下代码添加了一个云功能.它在函数部分正确显示(我假设),名称为 testItOut 并在 /blarg/{eventId}<的事件 document.update 上触发/code>.:

I then added a cloud function with the following code. It shows up (I assume) correctly in the functions section, with the name testItOut and triggering on the event document.update for /blarg/{eventId}.:

const functions = require('firebase-functions');
const os = require('os');
const fs = require('fs');
const path = require('path');
require('firebase-admin').initializeApp();

exports.testItOut = functions.firestore
    .document('blarg/{docId}')
    .onUpdate((change, context) => {
    console.log( "Inside #testItOut" );
    const projectId = 'testing-60477';
    const Storage = require('@google-cloud/storage');
    const storage = new Storage({
        projectId,
    });
    let bucketName = 'signatures'; 
    let fileName = 'temp.txt';
    const tempFilePath = path.join(os.tmpdir(), fileName);
    console.log( `Writing out to ${tempFilePath}` );
    fs.writeFileSync(tempFilePath, "something!" );

    return storage
        .bucket( bucketName )
        .upload( tempFilePath )
        .then( () => fs.unlinkSync(tempFilePath) )
        .catch(err => console.error('ERROR inside upload: ', err) );
    });

package.json 看起来像这样:

The package.json looks like this:

{
  "name": "functions",
  "description": "Cloud Functions for Firebase",
  "scripts": {
    "lint": "eslint .",
    "serve": "firebase serve --only functions",
    "shell": "firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "dependencies": {
    "@google-cloud/storage": "^1.7.0",
    "firebase-admin": "~5.12.1",
    "firebase-functions": "^1.0.3"
  },
  "devDependencies": {
    "eslint": "^4.12.0",
    "eslint-plugin-promise": "^3.6.0"
  },
  "private": true
}

如果我更改键humpty"的值,我会看到函数调用.但是,我在日志中得到了错误.

If I change the value of the key "humpty" I see the function invocation. But, I get the error inside the logs.

ERROR inside upload:  { ApiError: testing-60477@appspot.gserviceaccount.com does not have storage.objects.create access to signatures/temp.txt.
    at Object.parseHttpRespBody (/user_code/node_modules/@google-cloud/storage/node_modules/@google-cloud/common/src/util.js:193:30)
    at Object.handleResp (/user_code/node_modules/@google-cloud/storage/node_modules/@google-cloud/common/src/util.js:131:18)
...

我认为这很简单.我究竟做错了什么?我认为调用 initializeApp() 可以让我的函数权限自动写入帐户的存储桶?

This is as simple as it can get, I'd assume. What am I doing wrong? I thought calling initializeApp() gave my function rights to write to the storage bucket for the account automatically?

我看到的唯一奇怪的错误是未设置结算.我认为只有在您使用外部 API 时才有必要这样做.Google Storage 是外部 API"吗?日志消息并未表明这是问题所在.

The only strange error I see is that billing is not setup. I thought this was necessary only if you use external APIs. Is Google Storage an "external API?" The log message does not indicate that's the issue.

推荐答案

问题是我错误地认为调用bucket是在bucket里面设置子目录.而不是 bucket('signatures') 我应该把它变成一个像 bucket() 这样的空调用,然后提供一个选项参数(像 { destination: '/signatures/temp.txt' }) 用于 upload 调用:

The issue was that I mistakenly thought the call to bucket was to set the subdirectory inside the bucket. Instead of bucket('signatures') I should have made it an empty call like bucket() and then provided an options parameter (like { destination: '/signatures/temp.txt' }) for the upload call:

const functions = require('firebase-functions');
const os = require('os');
const fs = require('fs');
const path = require('path');
const admin = require('firebase-admin');
admin.initializeApp();

exports.testItOut = functions.firestore
    .document('blarg/{docId}')
    .onUpdate((change, context) => {
    console.log( "Inside #testItOut" );
    const storage = admin.storage()
    let fileName = 'temp.txt';
    let destination = '/signatures/temp.txt';
    const tempFilePath = path.join(os.tmpdir(), fileName);
    console.log( `Writing out to ${tempFilePath}` );
    fs.writeFileSync(tempFilePath, "something!" );

    return storage
        .bucket()
        .upload( tempFilePath, { destination } )
        .then( () => fs.unlinkSync(tempFilePath) )
        .catch(err => console.error('ERROR inside upload: ', err) );
    });

我很困惑,因为我在 firebase 文档中看到了很多调用 bucket("albums") 的示例(例如:https://cloud.google.com/nodejs/docs/reference/storage/1.6.x/Bucket#getFiles) 并假设这意味着存储桶可用于为上传设置 子目录.现在对我来说很明显不同了,但我被抛弃了,因为对存储桶的调用不像我在 firebase 存储控制台中看到的 bucketname-12345.appspot.com.

I got confused because I saw lots of examples in firebase docs where there are calls to bucket("albums") (like this: https://cloud.google.com/nodejs/docs/reference/storage/1.6.x/Bucket#getFiles) and assumed this meant the bucket can be used to set a subdirectory for uploads. It's obvious to me now the difference, but I was thrown off because the calls to bucket did not look like bucketname-12345.appspot.com as I see in the firebase storage console.

这篇关于如何使用从firestore触发的firebase云功能写入云存储桶?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆