【问题标题】:Uploading to Firebase Storage from a Google Cloud Function从 Google Cloud 函数上传到 Firebase 存储
【发布时间】:2021-04-10 19:00:15
【问题描述】:

我正在尝试创建一个 Firebase 函数,该函数允许我传入一组图像 URL 以创建蒙太奇,将文件上传到 Firebase 存储,然后返回生成的下载 URL。这将从我的应用程序中调用,因此我使用的是functions.https.onCall

const functions = require("firebase-functions");
const admin = require('firebase-admin');
var gm = require('gm').subClass({imageMagick: true});
admin.initializeApp();


exports.createMontage = functions.https.onCall((data, context) => {
var storageRef = admin.storage().bucket( 'gs://xyz-zyx.appspot.com' );
var createdMontage = storageRef.file('createdMontage.jpg');
function generateMontage(list){
  let g = gm()
   list.forEach(function(p){
       g.montage(p);
   })
   g.geometry('+81+81')
   g.density(5000,5000)
   .write(createdMontage, function(err) {
       if(!err) console.log("Written montage image.");
   });
   return true
}

generateMontage(data)

return createdMontage.getDownloadURL();

});

函数 generateMontage() 在 NodeJs 上本地工作(具有本地写入目标)。

谢谢。

【问题讨论】:

  • 我认为您应该在SO 上查看此相关问题。提到您尝试访问的存储对象没有 ref() 函数,只有 app 和 bucket()。
  • 我试过bucket()。答案中的第一个链接已失效。当我点击 API 并发现:cloud.google.com/storage/docs/samples/storage-upload-file,这 1) 似乎没有通过admin 和 2) 这意味着我必须命名并上传一个文件。我不知道如何.write(filename)
  • 文件会在公用文件夹中吗?如allow read; 中的存储规则。
  • 我一般都能发帖。只是不在这个。这不是规则问题。

标签: firebase google-cloud-functions google-cloud-storage firebase-storage


【解决方案1】:

我认为您可以将来自 gm 模块的输出流通过管道传输到 firebase storage 对象写入流。

const functions = require("firebase-functions");
const admin = require('firebase-admin');
var gm = require('gm').subClass({imageMagick: true});
admin.initializeApp();

exports.createMontage = functions.https.onCall(async (data, context) => {
   var storage = admin.storage().bucket( 'gs://xyz-zyx.appspot.com' );
   
   var downloadURL = await new Promise((resolve, reject) => {
         let g = gm()
         list.forEach(function(p){
             g.montage(p);
         })
         g.geometry('+81+81')
         g.density(5000,5000)
          .stream((err, stdout, stderr) => {
              if (err) {
                  reject();
              }
              stdout.pipe(
                  storage.file('generatedMotent.png).createWriteStream({
                    metadata: {
                        contentType: 'image/png',
                    },
                })
              ).on('finish', () => {
                storage
                    .file('generatedMotent')
                    .getSignedUrl({
                        action: 'read',
                        expires: '03-09-2491', // Non expring public url
                    })
                    .then((url) => {
                        resolve(url);
                    });
              });
         })
   });

   return downloadURL;
});

仅供参考,Firebase Admin SDK 存储对象没有getDownloadURL() 功能。 您应该从存储对象生成不会过期的公共签名 URL。

此外,根据issue,一段时间后应该会引起另一个问题。 要解决此问题,您应该使用永久服务帐户初始化 Firebase 应用。

const admin = require('firebase-admin');
const serviceAccount = require('../your-service-account.json');

admin.initializeApp({
    credential: admin.credential.cert(serviceAccount),
    projectId: JSON.parse(process.env.FIREBASE_CONFIG).projectId,
    databaseURL: JSON.parse(process.env.FIREBASE_CONFIG).databaseURL,
    storageBucket: JSON.parse(process.env.FIREBASE_CONFIG).storageBucket,
});

【讨论】:

  • 这不行,因为GM不带URL,所以你需要将图片下载到本地临时目录,然后通过GM运行这些临时文件,这被证明是最困难的部分这个的。不过谢谢。
【解决方案2】:

查看文档中的这个示例:

https://cloud.google.com/storage/docs/uploading-objects#storage-upload-object-code-sample

2021-01-11 更新

这是一个工作示例。我正在使用常规的 Cloud Functions,它的局限性在于 srcObjectdstObjectbucketName 是常量,但它确实创建了蒙太奇,这是您的目标。

PROJECT=[[YOUR-PROJECT]]
BILLING=[[YOUR-BILLING]]
REGION=[[YOUR-REGION]]

FUNCTION=[[YOUR-FUNCTION]]

BUCKET=[[YOUR-BUCKET]]
OBJECT=[[YOUR-OBJECT]] # Path from ${BUCKET} root

gcloud projects create ${PROJECT}

gcloud beta billing projects link ${PROJECT} \
--billing-account=${BILLING}

gcloud services enable cloudfunctions.googleapis.com \
--project=${PROJECT}

gcloud services enable cloudbuild.googleapis.com \
--project=${PROJECT}

gcloud functions deploy ${FUNCTION} \
--memory=4gib \
--max-instances=1
--allow-unauthenticated \
--entry-point=montager \
--set-env-vars=BUCKET=${BUCKET},OBJECT=${OBJECT} \
--runtime=nodejs12 \
--trigger-http \
--project=${PROJECT} \
--region=${REGION}

ENDPOINT=$(\
  gcloud functions describe ${FUNCTION} \
  --project=${PROJECT} \
  --region=${REGION} \
  --format="value(httpsTrigger.url)")

curl \
--request GET \
${ENDPOINT}


`package.json`:
```JSON
{
  "name": "montage",
  "version": "0.0.1",
  "dependencies": {
    "@google-cloud/storage": "5.7.1",
    "gm": "^1.23.1"
  }
}

还有index.js:

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

const gm = require('gm').subClass({ imageMagick: true });

const bucketName = process.env["BUCKET"];
const srcObject = process.env["OBJECT"];
const dstObject = "montage.png";

// Creates 2x2 montage
const list = [
  `/tmp/${srcObject}`,
  `/tmp/${srcObject}`,
  `/tmp/${srcObject}`,
  `/tmp/${srcObject}`
];

const montager = async (req, res) => {
  // Download GCS `srcObject` to `/tmp`
  const f = await storage
    .bucket(bucketName)
    .file(srcObject)
    .download({
      destination: `/tmp/${srcObject}`
    });

  // Creating GCS write stream for montage
  const obj = await storage
    .bucket(bucketName)
    .file(dstObject)
    .createWriteStream();

  let g = gm();
  list.forEach(f => {
    g.montage(f);
  });

  console.log(`Returning`);
  g
    .geometry('+81+81')
    .density(5000, 5000)
    .stream()
    .pipe(obj)
    .on(`finish`, () => {
      console.log(`finish`);
      res.status(200).send(`ok`);
    })
    .on(`error`, (err) => {
      console.log(`error: ${err}`);
      res.status(500).send(`uhoh!`);
    });
}
exports.montager = montager;

【讨论】:

  • 是的,我有。它仍然没有回答我的问题。我可以正常上传东西。你的建议是先创建一个const montage;,然后在函数中创建.write(),然后上传那个文件?
  • 我误解了,因为您说本地写入有效,并假设您无法将结果写入云存储。您需要在内存中创建文件,但随后可以使用流方法创建 GCS 对象。我写了this 一段时间,但原则应该仍然有效。请参阅index.js,从第 34 行开始。也会为你查找方法。
  • 谢谢你,你的文章澄清了一点。我仍在尝试围绕createReadStreamcreateWriteStream 以及您第27 行的函数,但似乎(来自您文章中的链接)我需要使用const tempFilePath = path.join(os.tmpdir(), fileName); 创建一个临时目录。写信,上传,然后取消链接。读/写流只是做同样事情的语法糖吗?
  • 在我的示例中,我从 GCS 对象压缩文件开始,处理文件(解压缩),然后将解压缩的内容写入输出存储桶。您应该能够在内存中创建蒙太奇并将其直接流式传输到 GCS 对象(文件)。您可以写入临时目录,但您不需要这样做。
【解决方案3】:

我从未使用过'gm',但根据它的npm page,它有一个toBuffer 功能。

所以也许这样的事情可以工作:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const gm = require('gm').subClass({ imageMagick: true });
admin.initializeApp();

exports.createMontage = functions.https.onCall((data, _context) => {
  const bucketName = 'xyz-zyx'; // not sure, I've always used the default bucket
  const bucket = admin.storage().bucket(bucketName);
  const storagePath = 'createdMontage.jpg';
  const fileRef = bucket.file(storagePath);
  const generateMontage = async (list) => {
    const g = gm();
    list.forEach(function (p) {
      g.montage(p);
    });
    g.geometry('+81+81');
    g.density(5000, 5000);
    return new Promise(resolve => {
      g.toBuffer('JPG', (_err, buffer) => {
        const saveTask = fileRef.save(buffer, { contentType: 'image/jpeg' });
        const baseStorageUrl = `https://firebasestorage.googleapis.com/v0/b/${bucket.name}/o/`;
        const encodedPath = encodeURIComponent(storagePath);
        const postfix = '?alt=media'; // see stackoverflow.com/a/58443247/6002078
        const publicUrl = baseStorageUrl + encodedPath + postfix;
        saveTask.then(() => resolve(publicUrl));
      });
    });
  };
  return generateMontage(data);
});

但它似乎可以更容易地完成。正如 Methkal Khalawi 所说:

这是full example on how to use ImageMagic with Functions。虽然他们用它来模糊图像,但想法是一样的。以及文档中的here is a tutorial

【讨论】:

  • 谢谢你,但这段代码不断给我错误,我不确定如何处理 - 他们希望在 .saveTask.then(()) 之后有一个错误处理程序。跨度>
  • @ezrabutler 谁是“他们”?尝试在该行添加.catch(console.error)
  • 谷歌函数日志。我之前添加了.catch((error) => {console.error(error);});,并且我必须更改存储桶名称,但实际上我看到正在创建一个文件并将其放入存储桶中,但是它是空的,即没有创建蒙太奇。我在 const list = [] 中硬编码了四个图像 URL,以尽量限制变量。看看是否有一个。
  • 文档说gm.montage() 只接受文件路径。你把外部网址?这在本地节点服务器中有效吗?
  • 这里是full example on how to use ImageMagic with Functions。以为他们用它来模糊图像,但想法是一样的。这是文档中的tutorial
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-30
  • 2019-06-11
  • 1970-01-01
  • 1970-01-01
  • 2019-06-21
  • 2019-12-29
  • 1970-01-01
相关资源
最近更新 更多