在Firebase函数中写入/tmp文件不工作



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

我的Firebase函数使用axios下载映像,使用fs将映像写入/tmp。然后我使用fs读取图像,将其转换为readableStream并将其发送给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);

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

我试着用很多方法下载并保存文件,但都不起作用。此外,根据我所读到的,Firebase函数允许从/tmp中写入和读取临时文件。

任何建议将不胜感激。请注意,我对NodeJS和Firebase非常陌生,所以请原谅我的基本代码。

多谢!

我无法看到您正在初始化目录正如这篇文章所建议的:

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

另外,请考虑如果您使用两个函数,/tmp目录将不会被共享,因为每个函数都有自己的目录。下面是道格·史蒂文森的解释。在相同的答案中,有一个很好的视频解释了本地和全局作用域以及如何使用tmp目录:

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

我建议使用扩展了云功能的Google Cloud Storage来实现你的目标。

最新更新