用于文件上传的Firebase云功能

时间:2017-07-14 08:30:51

标签: javascript firebase google-cloud-storage google-cloud-functions

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

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>

2 个答案:

答案 0 :(得分:13)

我从 Firebase支持小组 获得了解决方案

首先,该代码包含对“强大”的一个小误解。图书馆。这一行:

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

表示我们期待这种强大的&#39;使用其原始名称上传我们的文件,将其存储在具有随机名称的临时目录中。实际上,什么是强大的&#39;是将文件上传到存储在&#39; file.path&#39;中的随机路径,而不再使用原始文件名。如果我们想要,我们可以通过阅读&#39; file.name&#39;来了解原始文件名。

因此,代码的快速修复更改:

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

只是:

var filePath = file.path;

其次,还有一个问题可能导致此代码出现问题:该函数在&amp; form.parse(...)&#39;中完成异步工作之前终止。完成了。这意味着实际的文件上传可能会在函数已经完成时发生,因此它不再保留任何CPU或内存 - 上传速度会非常慢,甚至可能会失败。 / p>

解决方法是将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 Storage for Firebase允许您直接将文件上传到它,并且可以更好地工作:
它有访问控制
它具有可恢复的上传/下载(非常适合连接不良)
它可以接受任何大小的文件而不会出现超时问题 如果云功能应该响应文件上传而运行,那么你可以做更多的事情

答案 1 :(得分:1)

我通过文件下载到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

我认为我的代码达到了你想要完成的目标。我希望这有帮助;如有必要,我可以提供更多代码。