在 Firebase 函数中的 /tmp 中写入文件不起作用

Mae*_*ros 5 node.js firebase google-cloud-functions axios pinata

我正在编写一个 Firebase 函数,该函数使用express. 当端点被调用时,它需要从外部API下载图像并使用该图像进行第二次API调用。第二个 API 调用需要将图像作为 readableStream. 具体来说,我正在调用Pinata APIpinFileToIPFS的端点。

我的 Firebase 功能用于axios下载图像并将fs图像写入/tmp. 然后我用来fs读取图像,将其转换为 areadableStream并将其发送到 Pinata。

我的代码的精简版本如下所示:

const functions = require("firebase-functions");
const express = require("express");
const axios = require("axios");
const fs = require('fs-extra')
require("dotenv").config();
const key = process.env.REACT_APP_PINATA_KEY;
const secret = process.env.REACT_APP_PINATA_SECRET;
const pinataSDK = require('@pinata/sdk');
const pinata = pinataSDK(key, secret);
const app = express();

const downloadFile = async (fileUrl, downloadFilePath) => {
  try {
      const response = await axios({
        method: 'GET',
        url: fileUrl,
        responseType: 'stream',
      });


    // pipe the result stream into a file on disc
    response.data.pipe(fs.createWriteStream(downloadFilePath, {flags:'w'}))

    // return a promise and resolve when download finishes
    return new Promise((resolve, reject) => {
      response.data.on('end', () => {
        resolve()
      })

      response.data.on('error', () => {
        reject()
      })
    })

  } catch (err) { 
    console.log('Failed to download image')
    console.log(err);
    throw new Error(err);
  }
}; 

app.post('/pinata/pinFileToIPFS', cors(), async (req, res) => {
  const id = req.query.id;

  var  url = '<URL of API endpoint to download the image>';

  await fs.ensureDir('/tmp');

  if (fs.existsSync('/tmp')) {
    console.log('Folder: /tmp exists!')
  } else {
    console.log('Folder: /tmp does not exist!')
  }

  var filename = '/tmp/image-'+id+'.png';
  downloadFile(url, filename);

  if (fs.existsSync(filename)) {
    console.log('File: ' + filename + ' exists!')
  } else {
    console.log('File: ' + filename + ' does not exist!')
  }

  var image = fs.createReadStream(filename);

  const options = {
    pinataOptions: {cidVersion: 1}
  };

  pinata.pinFileToIPFS(image, options).then((result) => {
      console.log(JSON.stringify(result));
      res.header("Access-Control-Allow-Origin", "*");
      res.header("Access-Control-Allow-Headers", "Authorization, Origin, X-Requested-With, Accept");
      res.status(200).json(JSON.stringify(result));   
      res.send();
  }).catch((err) => {
      console.log('Failed to pin file');
      console.log(err);
      res.status(500).json(JSON.stringify(err));   
      res.send();
  });

});

exports.api = functions.https.onRequest(app);

Run Code Online (Sandbox Code Playgroud)

有趣的是,我的调试消息告诉我该/tmp文件夹存在,但我下载的文件的文件在文件系统中不存在。 [Error: ENOENT: no such file or directory, open '/tmp/image-314502.png']。注意,当我手动访问图片的URL时,可以正确访问图片。

我尝试使用多种方法下载并保存文件,但都不起作用。另外,根据我所读到的内容,Firebase Functions 允许从/tmp.

任何建议将被认真考虑。请注意,我对 NodeJS 和 Firebase 非常陌生,所以请原谅我的基本代码。

非常感谢!

Ale*_*lex 0

我无法看到您正在按照这篇文章中的建议初始化目录

const bucket = gcs.bucket(object.bucket);
const filePath = object.name;
const fileName = filePath.split('/').pop();
const thumbFileName = 'thumb_' + fileName;

const workingDir = join(tmpdir(), `${object.name.split('/')[0]}/`);//new
const tmpFilePath = join(workingDir, fileName);
const tmpThumbPath = join(workingDir, thumbFileName);

await fs.ensureDir(workingDir);
Run Code Online (Sandbox Code Playgroud)

另外,请考虑,如果您使用两个函数,则该/tmp目录不会共享,因为每个函数都有自己的目录。这是Doug Stevenson 的解释。在同一个答案中,有一个关于本地和全局范围以及如何使用 tmp 目录的很好解释的视频:

Cloud Functions 仅允许在特定服务器实例中一次运行一个函数。并行运行的函数在不同的服务器实例上运行,这些实例具有不同的 /tmp 空间。每个函数调用都彼此完全隔离地运行。您应该始终清理在 /tmp 中写入的文件,以免它们累积并导致服务器实例随着时间的推移耗尽内存。

我建议使用通过 Cloud Functions 扩展的 Google Cloud Storage来实现您的目标。