微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用 Firebase Cloud Functions 将文件上传到 Firebase 存储时出错

如何解决使用 Firebase Cloud Functions 将文件上传到 Firebase 存储时出错

我正在尝试使用 Firebase Cloud Functions 将 pdf 文件上传到 Firebase 存储,我有一个具有以下正文的 post 函数

{
    "email":"gianni@test.it","name":"Gianni","surname":"test","cellphone":"99999999","data": 
    {
    "file":BASE_64,"fileName":"test.pdf"
    }
}

我想在“file”字段中保存base64值并命名为“fileName”字段,这里是保存文件函数

const admin = require("firebase-admin");
  /**
   * Create a new file in the storage.
   * @param {Object} wrapper [File to upload in the storage.]
   * @param {String} path [Path to upload file to.]
   * @return {object} [Containing the response]
   */
  postStorageFileAsync(wrapper,path) {
    return new Promise((res,rej)=>{
      System.prototype.writeLog({
        wrapper: wrapper,path: path,});
      const newFile = admin.storage().bucket().file(wrapper.path);
      return newFile.save(wrapper.file).then((snapshot) => {
        System.prototype.writeLog({snap: snapshot});
        return snapshot.ref.getDownloadURL().then((downloadURL) => {
          System.prototype.writeLog({fileUrl: downloadURL});
          return res({code: 200,data: {url: downloadURL}});
        });
      }).catch((err)=>{
        System.writeLog(err);
        return rej(err);
      });
    });
  }

但我得到:

后课程

错误:必须指定文件名。在 Bucket.file (/workspace/node_modules/@google-cloud/storage/build/src/bucket.js:1612:19) at /workspace/Firebase/Firebase.js:43:48 at new Promise () at Object。 postStorageFileAsync (/workspace/Firebase/Firebase.js:38:12) at /workspace/PersistanceStorage/PersistanceStorage.js:407:35 at processticksAndRejections (internal/process/task_queues.js:97:5)

除了错误本身,是否有人有关于如何通过函数文件上传到 firebase 存储的工作示例/教程链接?文档真的很缺乏。

谢谢

解决方法

除了@Renaud 的评论之外,Firebase Admin SDK 还依赖于 Cloud Storage client library 来访问存储桶。如果您查看 Nodejs API 文档,您会看到 getDownloadURL() 不存在,因此如果您想继续使用 Admin SDK 并获取下载 URL,您必须获取一次文件的元数据已上传。

这是我想出的示例代码:

const admin = require("firebase-admin");
const os = require('os');
const fs = require('fs');
const path = require('path');

...

const wrapper = {
  "file" : "BASE_64","fileName" : "filename.pdf",}
const pathName = "test/dir"

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

async function postStorageFileAsync(wrapper,pathName) {
  // Convert Base64 to PDF. You're only allowed to write in /tmp on Cloud Functions
  const tmp = `${os.tmpdir()}/converted.pdf`
  fs.writeFileSync(tmp,wrapper.file,'base64',(error) => {
    if (error) throw error;
  });   
  
  // Upload to GCS
  const target = path.join(pathName,wrapper.fileName);
  await bucket.upload(tmp,{
    destination: target
  }).then(res =>{
    fs.unlinkSync(tmp)
    console.log(`Uploaded to GCS`)
  })

  // Get Download URL
  const newFile =  await bucket.file(target); 
  const [metadata] = await newFile.getMetadata();
  const url = metadata.mediaLink;
  console.log(url);  
}

postStorageFileAsync(wrapper,pathName)

请注意,在此代码中,对象不是公开的,因此对 URL 的未授权访问将显示权限错误。如果您想让您的对象可供公众访问,请参阅 File.makePublic()

,

因此,根据 Donnald Cucharo 的回答,我将为像我这样仍在使用 javascript 引擎(不支持方法签名中的异步)而不是打字稿引擎的人发布我的工作解决方案:

步骤 1) 在您的项目中安装 @google-cloud/storage npm,据我所知,每个 Firebase 存储都构建在 Google 云存储实例之上。

第 2 步)按照 this guide 从您的 firebase 项目控制台生成一个管理密钥,以便将其上传到您的函数文件夹中,以授权您的函数项目对您的存储项目进行身份验证和通信。

第 3 步)使用以下代码(它与 Donnald Cucharo 的解决方案非常相似,但大量使用了 Promise):

const functions = require("firebase-functions");
const path = require("path");
const os = require("os");

  
class System {
  
  ...

  /**
   * Extract th extension of a file from it's path,and returns it.
   * @param {String} directory [Dir from where to extract the extension from.]
   * @return {String} [Containing the extension of the file from the directory or undefined]
   */
  getFileExtensionFromDirectory(directory) {
    const ext = path.extname(directory);
    if (ext === "") {
      return undefined;
    }
    return ext;
  }

  /**
   * Extract the name of the file from a directory.
   * @param {String} directory [Dir from where to extract the extension from.]
   * @return {String} [Containing the filename]
   */
  getFileNameFromDirectory(directory) {
    const extension = this.getFileExtensionFromDirectory(directory);
    if (extension === undefined) return extension;
    return path.basename(directory,extension);
  }

  /**
   * Returns the system temp directory.
   * @return {String} [Containing the system temporary directory.]
   */
  getSystemTemporarydirectory() {
    return os.tmpdir();
  }
   
}

module.exports = System;

然后,是时候实际上传文件了:

const admin = require("firebase-admin");
const functions = require("firebase-functions");
const bucket = admin.storage().bucket(functions.config().bucket.url);
const System = require("../System/System");
const fs = require("fs");

/**
 * Pure fabrication class to access the Firebase services
 */
class Firebase {

  /**
   * Create a new file in the storage.
   * @param {Object} wrapper [File to upload in the storage.]
   * @param {String} path [Path to upload file to.]
   * @return {object} [Containing the response]
   */
  postStorageFileAsync(wrapper) {
    return new Promise((res,rej)=>{
      if (wrapper.file === undefined) {
        return rej({code: 400,error:
          {it: "Devi forninre il valore in base64 del file che vuoi caricare.",en: "You must provide the base64 value of the file to upload."}});
      }


      if (wrapper.path === undefined) {
        return rej({code: 400,error:
          {it: "Devi fornire il percorso dove vuoi caricare il tuo file.",en: "Missing path filed in wrapper."}});
      }

      const fileName = System.prototype.getFileNameFromDirectory(wrapper.path);
      const fileExtension = System.prototype.getFileExtensionFromDirectory(wrapper.path);

      if (fileName === undefined || fileExtension === undefined) {
        return rej({code: 400,error:
          {it: "Formato del file non riconosciuto.",en: "Unrecognized file type or file name,the file should be in fileName.extension format"}});
      }

      const file = fileName + fileExtension;
      const tmpDirectory = System.prototype.getSystemTemporarydirectory() + "/" + file;

      return fs.promises.writeFile(tmpDirectory,"base64").then(()=>{
        const options = {
          destination: wrapper.path,};

        return bucket.upload(tmpDirectory,options).then(() =>{
          fs.unlinkSync(tmpDirectory);
          return this.getStorageFileFromPathAsync(wrapper.path).then((response)=>{
            return res(response);
          });
        });
      }).catch((err)=>{
        fs.unlinkSync(tmpDirectory);
        System.prototype.writeLog(err);
        return rej({code: 500,error: err});
      });
    });
  }

  /**
   * Retrieve the url of a file in the storage from the path.
   * @param {String} path [Path of the file to get the url]
   * @return {Object} [Containing te response]
   */
  getStorageFileFromPathAsync(path) {
    return new Promise((res,rej)=>{
      bucket.file(path).makePublic().then(()=>{
        bucket.file(path).getMetadata() .then((response)=>{
          System.prototype.writeLog({response: response});
          const metadata = response[0];
          System.prototype.writeLog({metadata: metadata});
          return res({code: 200,url: metadata.mediaLink});
        });
      }).catch((err)=>{
        System.prototype.writeLog(err);
        return rej({code: 500,error: err});
      });
    });
  }

}

module.exports = Firebase;

现在让我们谈谈代码,因为有些部分我很难理解,但主要的推理是这样的:

  1. 我们需要在系统的临时目录下创建一个目录,里面有一个空文件。

  2. 通过使用 fs 库,我们需要打开一个流并将数据转储到我们创建的临时目录中:

    fs.promises.writeFile(tmpDirectory,"base64").then(()=>{ ....})

  3. 我们现在可以使用我们在步骤 1-2 中创建的路径将文件上传到 firebase 存储中,但我们还无法访问下载 url。

  4. 因为在我们可以更改新创建的文件可见性并最终使用 getStorageFileFromPathAsync() 方法获取下载 url 之前,我们以管理员身份进行了身份验证。

希望对您有所帮助。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。