简体   繁体   English

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

[英]Error while uploading file to Firebase Storage using Firebase Cloud Functions

I'm trying to upload a pdf to Firebase Storage using Firebase Cloud Functions, I have this post function with the following body: I'm trying to upload a pdf to Firebase Storage using Firebase Cloud Functions, I have this post function with the following body:

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

I want to save the base64 value in the "file" field and name as "fileName" field, here is the function that saves the file:我想在“文件”字段中保存 base64 值并命名为“文件名”字段,这里是保存文件的 function:

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);
      });
    });
  }

But I'm getting:但我得到:

postCurriculum课后

Error: A file name must be specified.错误:必须指定文件名。 at 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)在 Bucket.file (/workspace/node_modules/@google-cloud/storage/build/src/bucket.js:1612:19) 在 /workspace/Firebase/Firebase.js:43:48 在新 Promise () 在 Z497031794414A552435F90151AC3B54. postStorageFileAsync (/workspace/Firebase/Firebase.js:38:12) at /workspace/PersistanceStorage/PersistanceStorage.js:407:35 at processTicksAndRejections (internal/process/task_queues.js:97:5)

Aside for the error itself, does anybody has a working example/tutorial link on how to upload files to firebase storage through functions?除了错误本身,是否有人有关于如何通过函数将文件上传到 firebase 存储的工作示例/教程链接? The documentation is really lacking.文档真的很缺乏。

Thanks谢谢

In addition to @Renaud's comment, Firebase Admin SDK relies on Cloud Storage client library to access storage buckets.除了@Renaud 的评论,Firebase Admin SDK 依赖Cloud Storage 客户端库来访问存储桶。 If you check the Nodejs API doc, you'll see that getDownloadURL() does not exist so if you want to continue using the Admin SDK and get the download URL, you have to get the metadata of the file once it's uploaded. If you check the Nodejs API doc, you'll see that getDownloadURL() does not exist so if you want to continue using the Admin SDK and get the download URL, you have to get the metadata of the file once it's uploaded.

Here's a sample code I came up with:这是我想出的示例代码:

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)

Note that in this code the object is not public, so unauthorized access to the URL will display a permission error.请注意,在此代码中 object 不是公开的,因此未经授权访问 URL 将显示权限错误。 If you want to make your objects accessible to public, see File.makePublic() .如果您想让您的对象可供公众访问,请参阅File.makePublic()

So based upon Donnald Cucharo answer I will post my working solution for the people like me that are still using the javascript engine (that does not support the async in method's signature) instead of the typescript engine:因此,根据 Donnald Cucharo 的回答,我将为像我这样仍在使用 javascript 引擎(不支持方法签名中的异步)而不是 typescript 引擎的人发布我的工作解决方案:

Step 1) Install the @google-cloud/storage npm in your project, from what I understood every firebase storage is built on top of a google cloud storage instance.步骤 1)在您的项目中安装@google-cloud/storage npm,据我了解,每个 firebase 存储都构建在谷歌云存储实例之上。

Step 2) Follow this guide to generate an admin key from your firebase project console in order to upload it inside your functions folder to authorize your functions project to authenticate and comunicate with your storage project.步骤 2) 按照本指南从您的 firebase 项目控制台生成一个管理员密钥,以便将其上传到您的函数文件夹中,以授权您的函数项目与您的存储项目进行身份验证和通信。

Step 3) Use the following code (it is very similar to Donnald Cucharo's solution but with heavy use of promises):第 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;

then, it's time for the actual uploading of the file:然后,是时候实际上传文件了:

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, wrapper.file, "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;

Now let's talk about the code, because there were some parts that I've struggle to understand, but the main reasoning is something like this:现在让我们谈谈代码,因为有些部分我很难理解,但主要原因是这样的:

  1. We need to create a directory with an empty file in the temp directory of the system.我们需要在系统的 temp 目录中创建一个包含空文件的目录。

  2. By using the fs library we need to open a stream and dump the data in the temp directory we created:通过使用 fs 库,我们需要打开 stream 并将数据转储到我们创建的临时目录中:

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

  3. We can now upload the file in the firebase storage by using the path we created in step 1-2 but we can't access the download url yet.我们现在可以使用我们在步骤 1-2 中创建的路径将文件上传到 firebase 存储中,但我们还无法访问下载 url。

  4. As we authenticated as admins before we can just change the newly created file visibility and finally get the download url by using the getStorageFileFromPathAsync() method.由于我们在通过使用 getStorageFileFromPathAsync() 方法更改新创建的文件可见性并最终获得下载 url 之前以管理员身份进行了身份验证。

I hope it was helpful.我希望它是有帮助的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Firebase存储错误的云功能 - Cloud Functions for Firebase Storage Error 从 REST API 获取图像并将其上传到 Firebase 云存储使用 Z035489FF8D324159ZEEA 功能 Cloud Storage - Getting image from REST API and uploading it to Firebase Cloud Storage using Firebase Cloud Functions 使用下载 url 和 Cloud Functions 从 Firebase 存储中删除文件 - Delete a file from firebase storage using download url with Cloud Functions 使用Cloud Functions REST API请求将文件存储在Firebase存储中 - Store file in Firebase Storage using Cloud Functions REST API request 使用云功能创建pdf文件并将其上传到firebase存储? (可以上传本地文件,但不能从云功能上传) - Create and upload pdf file to firebase storage using cloud functions? (can upload locally file, but not from cloud functions) 使用http cloud云功能从Firebase云存储下载文件时,没有此类文件或目录 - no such file or directory when downloading a file from firebase cloud storage using http cloud cloud functions 从firebase函数上传数据到firebase存储? - Uploading data from firebase functions to firebase storage? 上传到Firebase存储时如何使用Cloud Functions for FIrebase获取mp3文件的持续时间 - How to get the duration of a mp3 file using Cloud Functions for FIrebase when uploaded to firebase storage Firebase 云功能:部署 function 时出错 - Firebase Cloud Functions: Error while deploying function Firebase 云存储上传文件路径错误 - Firebase Cloud Storage Upload File Path Error
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM