web-dev-qa-db-fra.com

Fonction cloud Firebase pour le téléchargement de fichiers

J'ai cette fonction cloud que j'ai écrite pour télécharger un fichier sur Google Cloud Storage:

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

J'ai utilisé formidable pour analyser le fichier téléchargé et j'ai essayé de consigner les propriétés du fichier téléchargé et cela semble correct; il est téléchargé dans un répertoire temporaire '/tmp/upload_2866bbe4fdcc5beb30c06ae6c3f6b1aa/ mais lorsque j'essaie de télécharger le fichier sur le gcs, j'obtiens cette erreur:

{ 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' }

J'utilise ce formulaire html pour télécharger le fichier:

<!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>
14
dsharew

J'ai obtenu une solution de la part de l'équipe d'assistance Firebase

Premièrement, le code contient un petit malentendu sur la bibliothèque "formidable". La ligne:

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

indique que nous nous attendons à ce que "formidable" télécharge notre fichier avec son nom d'origine, le stockant dans un répertoire temporaire avec un nom aléatoire. En fait, ce que "formidable" fait est de télécharger le fichier vers un chemin aléatoire stocké dans "file.path", sans plus utiliser le nom de fichier d'origine. Si nous voulons, nous pouvons découvrir quel était le nom de fichier d'origine en lisant 'file.name'.

La solution rapide pour le code est donc de changer:

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

Pour être juste:

var filePath = file.path;

Deuxièmement, il y a un autre problème qui peut provoquer ce problème de code: la fonction se termine avant que le travail asynchrone effectué dans 'form.parse (...)' ne soit terminé. Cela signifie que le téléchargement du fichier se produira probablement lorsque la fonction a déjà dit que c'est fait, donc il n'y a plus de CPU ou de mémoire réservée - le téléchargement se fera très lentement, ou peut-être même échouera.

Le correctif consiste à envelopper la form.parse(...) dans une promesse:

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

Enfin, vous voudrez peut-être envisager d'utiliser Cloud Storage for Firebase pour télécharger votre fichier au lieu des fonctions Cloud. Cloud Storage for Firebase vous permet de télécharger des fichiers directement sur celui-ci et fonctionnerait beaucoup mieux:
Il a un contrôle d'accès
Il a des téléchargements/téléchargements de reprise (idéal pour une mauvaise connectivité)
Il peut accepter des fichiers de toute taille sans délai d'attente
Si une fonction cloud doit s'exécuter en réponse à un téléchargement de fichier, vous pouvez le faire et bien plus encore

14
dsharew

J'ai réussi cela en téléchargement le fichier dans le tmp à la place.

Tu auras besoin de:

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

Ensuite, à l'intérieur de onChange. J'ai créé tempLocalDir comme ceci:

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

Ensuite, j'utilise mkdirp pour créer le répertoire temporaire

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

Je pense que mon code atteint ce que vous tentiez d'accomplir. J'espère que ça aide; Je peux offrir plus de code si nécessaire.

1
Drei