从Firebase云函数上传文件到云存储

18

这份文档对我来说太过复杂。它展示了如何从云存储下载文件到云函数,操纵文件,然后上传新文件到云存储。我只想要看到将文件从云函数上传到云存储的基本最小指令。为什么这样不起作用:

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

admin.initializeApp();

exports.storage = functions.firestore.document('Test_Value').onUpdate((change, context) => {

  var metadata = {
    contentType: 'text',
  };

  admin.storage().ref().put( {'test': 'test'}, metadata)
  .then(function() {
    console.log("Document written.");
  })
  .catch(function(error) {
    console.error(error);
  })

});

错误信息是admin.storage(...).ref不是一个函数。我猜测firebase-admin包含Firestore但不包含Storage?我是否应该使用@google-cloud/storage而不是firebase-admin?为什么这个方法行不通:

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

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

admin.initializeApp();

exports.storage = functions.firestore.document('Test_Value').onUpdate((change, context) => {

  storage.bucket().upload( {'test': 'test'} , {
    metadata: {
      contentType: 'text'
    }
  })

});

我甚至无法部署这段代码,错误信息是

Error parsing triggers: Cannot find module './clone.js'

显然缺少一个npm模块依赖?但这个模块并不叫做clone.js?我尝试了需要child-process-promisepathosfs,但都没有解决clone.js的错误。

为什么admin.initializeApp();没有参数,而我在index.html文件中有:

firebase.initializeApp({
    apiKey: 'swordfish',
    authDomain: 'myapp.firebaseapp.com',
    databaseURL: "https://myapp.firebaseio.com",
    projectId: 'myapp',
    storageBucket: "myapp.appspot.com"
  });

我发现另一个问题:

npm list -g --depth=0       

/Users/TDK/.nvm/versions/node/v6.11.2/lib
├── child_process@1.0.2
├── UNMET PEER DEPENDENCY  error: ENOENT: no such file or directory, open '/Users/TDK/.nvm/versions/node/v6.11.2/lib/node_modules/firebase-admin/package.json
├── firebase-functions@2.1.0
├── firebase-tools@6.0.1
├── firestore-backup-restore@1.3.1
├── fs@0.0.2
├── npm@6.4.1
├── npm-check@5.9.0
├── protractor@5.4.1
├── request@2.88.0
└── watson-developer-cloud@3.13.0
换句话说,firebase-adminNode 6.11.2 出了问题。我应该使用一个 Node 版本管理器来回退到旧版本吗?

我也有同样的困扰,我猜我们应该使用这个API参考文档.. https://googleapis.dev/nodejs/storage/latest/File.html#save - Elona Mishmika
5个回答

13
  1. 前往https://console.cloud.google.com/iam-admin/iam
  2. 点击您的App Engine 默认服务帐户旁边的铅笔图标
  3. + 添加其他角色
  4. 添加Cloud Functions 服务代理

在我的具体用例中,我需要将base64字符串解码为字节数组,然后使用它保存图像。

    var serviceAccount = require("./../serviceAccountKey.json");

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

    admin.initializeApp({
        projectId: serviceAccount.project_id, 
        credential: admin.credential.cert(serviceAccount),
        databaseURL: "https://your_project_id_here.firebaseio.com", //update this
        storageBucket: "your_bucket_name_here.appspot.com" //update this
      });

    function uploadProfileImage(imageBytes64Str: string): Promise<any> {

        const bucket = admin.storage().bucket()
        const imageBuffer = Buffer.from(imageBytes64Str, 'base64')
        const imageByteArray = new Uint8Array(imageBuffer);
        const file = bucket.file(`images/profile_photo.png`);
        const options = { resumable: false, metadata: { contentType: "image/jpg" } }

        //options may not be necessary
        return file.save(imageByteArray, options)
        .then(stuff => {
            return file.getSignedUrl({
                action: 'read',
                expires: '03-09-2500'
              })
        })
        .then(urls => {
            const url = urls[0];
            console.log(`Image url = ${url}`)
            return url
        })
        .catch(err => {
            console.log(`Unable to upload image ${err}`)
        })
    }
然后您可以这样调用该方法并链接这些调用。
    uploadProfileImage(image_bytes_here)
    .then(url => {
        //Do stuff with the url here        
    })
注意:您必须使用服务帐户初始化管理员并指定默认存储桶。如果您仅执行admin.initializeApp(),则您的图像 URL 将在 10 天后过期。 正确使用服务帐户的步骤:
  1. 转到服务帐户并生成一个私钥
  2. 将 JSON 文件放入您的函数文件夹(与 src 和 node_modules 并列)
  3. 进入存储并复制不包括前面的 "gs://" 的 URL。在初始化管理员时使用此 URL 作为存储桶 URL。
  4. 使用上面的项目 ID 作为数据库 URL。

谢谢。我有点惊讶所有这些都是必要的。Firestore(数据库)开箱即用? - Boern

6

See Introduction to the Admin Cloud Storage API for further details on how to use the Cloud Storage service in Firebase Admin SDK.

var admin = require("firebase-admin");

var serviceAccount = require("path/to/serviceAccountKey.json");

admin.initializeApp({
    credential: admin.credential.cert(serviceAccount),
    storageBucket: "<BUCKET_NAME>.appspot.com"
});

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

// 'bucket' is an object defined in the @google-cloud/storage library.
// See https://googlecloudplatform.github.io/google-cloud-node/#/docs/storage/latest/storage/bucket
// for more details.

关于上传对象,请参见Cloud Storage文档上传对象示例代码:

// Imports the Google Cloud client library
const {Storage} = require('@google-cloud/storage');

// Creates a client
const storage = new Storage();

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// const bucketName = 'Name of a bucket, e.g. my-bucket';
// const filename = 'Local file to upload, e.g. ./local/path/to/file.txt';

// Uploads a local file to the bucket
await storage.bucket(bucketName).upload(filename, {
  // Support for HTTP requests made with `Accept-Encoding: gzip`
  gzip: true,
  metadata: {
    // Enable long-lived HTTP caching headers
    // Use only if the contents of the file will never change
    // (If the contents will change, use cacheControl: 'no-cache')
    cacheControl: 'public, max-age=31536000',
  },
});

console.log(`${filename} uploaded to ${bucketName}.`);

2
那段代码创建了一个存储桶,但没有将文件上传到存储中。 - Thomas David Kehoe
对于第二个代码块,将字段/值destination:'test/coffeeLogo.png'添加到**.upload()**选项JSON中即可完成文件上传-https://stackoverflow.com/a/54227249,并在此线程https://dev59.com/2lQJ5IYBdhLWcg3wwYz7#53181557中。 - Gene Bo

4

我通过谷歌云函数将硬盘中的文件上传到Firebase云存储。首先,我查阅了Google Cloud Functions的文档,了解如何使用bucket.upload方法。

    const functions = require('firebase-functions');
    const admin = require('firebase-admin');
    admin.initializeApp();
    
    exports.Storage = functions.firestore.document('Storage_Value').onUpdate((change, context) => {
    
      const {Storage} = require('@google-cloud/storage');
      const storage = new Storage();
      const bucket = storage.bucket('myapp.appspot.com');
    
      const options = {
        destination: 'Test_Folder/hello_world.dog'
      };
    
      bucket.upload('hello_world.ogg', options).then(function(data) {
        const file = data[0];
      });
    
      return 0;
    });

前三行是云函数样板代码。下一行

    exports.Storage = functions.firestore.document('Storage_Value').onUpdate((change, context) => {

创建Cloud Function并设置触发器。接下来的三行是Google Cloud的样板代码。

其余的代码定位了我电脑硬盘上的hello_world.ogg文件,它在我的项目目录的functions文件夹中,并将其上传到Test_Folder目录并将文件名更改为hello_world.dog在我的Firebase Cloud Storage中。这返回一个承诺,下一行const file = data[0];没有必要,除非你想对文件做其他事情。

最后我们return 0;。此行除了防止错误消息外无任何作用。

Function returned undefined, expected Promise or Value

0
对于来到这里的人,想知道为什么他们没有收到错误提示,但在本地调试时文件却不显示在在线控制台中。
解决方案:不要在本地启动存储模拟器。
也就是说,当你只想调试函数时:firebase emulators:start --inspect-functions --only functions

-1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接