文件上传的 Firebase 云功能

Posted

技术标签:

【中文标题】文件上传的 Firebase 云功能【英文标题】:Firebase cloud function for file upload 【发布时间】:2017-12-19 06:42:43 【问题描述】:

我有这个云功能,我写的上传文件到谷歌云存储:

const gcs = require('@google-cloud/storage')(keyFilename:'2fe4e3d2bfdc.json');

var filePath = file.path + "/" + file.name;

    return bucket.upload(filePath, 
        destination: file.name
    ).catch(reason => 
            console.error(reason);
    );

我使用formidable 来解析上传的文件,并尝试记录上传文件的属性,看起来不错;它已上传到临时目录'/tmp/upload_2866bbe4fdcc5beb30c06ae6c3f6b1aa/ 但是当我尝试将文件上传到 gcs 时出现此错误:

 Error: EACCES: permission denied, stat '/tmp/upload_2866bbe4fdcc5beb30c06ae6c3f6b1aa/thumb_ttttttt.jpg'
    at Error (native)
  errno: -13,
  code: 'EACCES',
  syscall: 'stat',
  path: '/tmp/upload_2866bbe4fdcc5beb30c06ae6c3f6b1aa/thumb_ttttttt.jpg' 

我正在使用这个 html 表单来上传文件:

<!DOCTYPE html>
<html>
<body>

<form action="https://us-central1-appname.cloudfunctions.net/uploadFile" method="post" enctype="multipart/form-data">
    Select image to upload:
    <input type="file" name="fileToUpload" id="fileToUpload">
    <input type="submit" value="Upload Image" name="submit">
</form>

</body>
</html>

【问题讨论】:

您似乎没有获得执行此操作的正确权限。 是的,我无法访问我上传的临时文件。 【参考方案1】:

我从 Firebase 支持团队

那里得到了解决方案

所以第一件事:

var filePath = file.path + "/" + file.name;

我们不需要 file.name,因为 file.path 是文件的完整路径(包括文件名)。

所以改为这样:

var filePath = file.path;

其次,函数在 'form.parse(...)' 中的异步工作完成之前终止。这意味着在函数执行结束时,实际的文件上传可能仍在进行中。

解决方法是将form.parse(...) 包装在一个承诺中:

exports.uploadFile = functions.https.onRequest((req, res) => 
   var form = new formidable.IncomingForm();
   return new Promise((resolve, reject) => 
     form.parse(req, function(err, fields, files) 
       var file = files.fileToUpload;
       if(!file)
         reject("no file to upload, please choose a file.");
         return;
       
       console.info("about to upload file as a json: " + file.type);
       var filePath = file.path;
       console.log('File path: ' + filePath);
 
       var bucket = gcs.bucket('bucket-name');
       return bucket.upload(filePath, 
           destination: file.name
       ).then(() => 
         resolve();  // Whole thing completed successfully.
       ).catch((err) => 
         reject('Failed to upload: ' + JSON.stringify(err));
       );
     );
   ).then(() => 
     res.status(200).send('Yay!');
     return null
   ).catch(err => 
     console.error('Error while parsing form: ' + err);
     res.status(500).send('Error while parsing form: ' + err);
   );
 );

最后,您可能需要考虑在上传文件时使用 Cloud Storage for Firebase 而不是 Cloud 函数。 Cloud Storage for Firebase 允许您将文件直接上传到它,而且效果会更好:

    它有访问控制 它具有可恢复的上传/下载(非常适合连接不良) 它可以接受任何大小的文件而不会出现超时问题 如果您想在文件上传时触发云功能,您可以 做到这一点,还有更多

【讨论】:

如果您使用 Cloud Storage for Firebase,您还能使用 Formidable 进行文件上传吗?你也可以在本地 Firebase 服务器上使用它吗? 是的,我用谷歌云存储试过,它可以工作,但我没有用本地 firebase 服务器试过。 谢谢。你能告诉我你是如何在 Node.js 服务器上使用 Formidable 来做到这一点的吗? 以上代码为云功能的完整代码,如果您尝试过功能请看这里:firebase.google.com/docs/functions 啊,谢谢。我错过了 Promise 中使用的强大功能!【参考方案2】:

我通过将文件下载tmp 来管理这个。

你需要:

const mkdirp = require('mkdirp-promise');

然后,在onChange 内部。我像这样创建了tempLocalDir

const LOCAL_TMP_FOLDER = '/tmp/';
const fileDir = (the name of the file); //whatever method you choose to do this
const tempLocalDir = `$LOCAL_TMP_FOLDER$fileDir`;

然后我使用 mkdirp 制作临时目录

return mkdirp(tempLocalDir).then(() => 
// Then Download file from bucket.
const bucket = gcs.bucket(object.bucket);
return bucket.file(filePath).download(
  destination: tempLocalFile
).then(() => 
    console.log('The file has been downloaded to', tempLocalFile);
    //Here I have some code that converts images then returns the converted image
    //Then I use
    return bucket.upload((the converted image), 
        destination: (a file path in your database)
    ).then(() => 
        console.log('JPEG image uploaded to Storage at', filePath);
    )//You can perform more actions of end the promise here

我认为我的代码实现了您想要实现的目标。我希望这有帮助;如有必要,我可以提供更多代码。

【讨论】:

以上是关于文件上传的 Firebase 云功能的主要内容,如果未能解决你的问题,请参考以下文章

使用 firebase 功能将图像上传到云存储错误

Flutter firebase 存储插件需要大量时间来上传文件

验证 Firebase 云功能中的存储文件数据

Firebase 云功能 [错误:超出内存限制。函数调用被中断。] 在 youtube 视频上传

将文件上传到 Firebase 云存储时出错:“rules_version = 1 不允许列出存储桶中的对象”

将 Firebase 存储下载 URL 推送到 Firebase 云 Firestore