为了确保它尽可能干净和隔离,我创建了一个新的测试项目,除了这些步骤外,其中没有任何其他内容。
然后我在控制台中启用了云存储。我创建了一个名为“ blarg”的新集合(名字叫eh!)。我向其中添加了一个文档,并使用了自动编号,然后添加了一个带有字符串dumpty的名为“ humpty”的字段。在控制台中看起来像这样:
我在firebase控制台的storage部分中创建了一个名为signatures
的目录。我想在我的函数(如下)触发时将文件写入此目录。
然后,我使用以下代码添加了一个云功能。它在功能部分正确显示(我假设),其名称为testItOut
,并为document.update
的事件/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看起来像这样:
{
"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”的值,则会看到函数调用。但是,我在日志中看到错误。
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()
赋予了我的功能权限以自动写入该帐户的存储桶?
我看到的唯一奇怪的错误是未设置计费。我认为只有在使用外部API时才有必要。 Google Storage是“外部API”吗?日志消息并不表示这是问题所在。
答案 0 :(得分:4)
问题是我错误地认为对bucket
的调用是在存储桶中设置子目录。代替bucket('signatures')
,我应该像bucket()
那样进行空调用,然后为{ destination: '/signatures/temp.txt' }
调用提供一个options参数(例如upload
):
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存储控制台中看到的对bucket的调用看起来不像bucketname-12345.appspot.com
。
答案 1 :(得分:0)
不要打扰尝试使用Google Cloud Storage SDK:
const Storage = require('@google-cloud/storage');
const storage = new Storage({
projectId,
});
相反,只需在初始化后使用Admin SDK。管理员SDK包装了Google Cloud Storage SDK。初始化admin时,还将隐式初始化它包装的所有组件。
const admin = require('firebase-admin')
admin.initializeApp()
const storage = admin.storage()
// Now use storage just like you would use @google-cloud/storage
在初始化admin时使用的默认服务帐户(没有类似args)应该对项目的默认存储桶具有写权限。