web-dev-qa-db-fra.com

Obtenir l'URL de téléchargement du fichier chargé avec Cloud Functions for Firebase

Après avoir téléchargé un fichier dans Firebase Storage avec Functions for Firebase, j'aimerais obtenir l'URL de téléchargement du fichier.

J'ai ceci :

...

return bucket
    .upload(fromFilePath, {destination: toFilePath})
    .then((err, file) => {

        // Get the download url of file

    });

Le fichier objet a beaucoup de paramètres. Même un nommé mediaLink. Cependant, si j'essaie d'accéder à ce lien, j'obtiens cette erreur:

Les utilisateurs anonymes n'ont pas accès à storage.objects.get to object ...

Quelqu'un peut-il me dire comment obtenir le téléchargement public URL?

Je vous remercie

52
Valentin

Vous devez générer une URL signée à l'aide de getSignedURL via le module @ google-cloud/storage NPM.

Exemple:

const gcs = require('@google-cloud/storage')({keyFilename: 'service-account.json'});
// ...
const bucket = gcs.bucket(bucket);
const file = bucket.file(fileName);
return file.getSignedUrl({
  action: 'read',
  expires: '03-09-2491'
}).then(signedUrls => {
  // signedUrls[0] contains the file's public URL
});

Vous devez initialiser @google-cloud/storage avec informations d'identification de votre compte de service }, car les informations d'identification par défaut de l'application ne seront pas suffisantes.

UPDATE: le kit de développement Cloud Storage SDK est désormais accessible via le kit de développement Firebase Admin SDK, qui agit en tant qu'encapsuleur autour de @ google-cloud/storage. La seule façon de le faire est si vous:

  1. Initiez le SDK avec un compte de service spécial, généralement via une seconde instance autre que celle par défaut.
  2. Ou, sans compte de service, en donnant au compte de service App Engine par défaut l'autorisation "signBlob".
77
James Daniels

Voici un exemple sur la façon de spécifier le jeton de téléchargement lors du téléchargement:

const UUID = require("uuid-v4");

const fbId = "<YOUR APP ID>";
const fbKeyFile = "./YOUR_AUTH_FIlE.json";
const gcs = require('@google-cloud/storage')({keyFilename: fbKeyFile});
const bucket = gcs.bucket(`${fbId}.appspot.com`);

var upload = (localFile, remoteFile) => {

  let uuid = UUID();

  return bucket.upload(localFile, {
        destination: remoteFile,
        uploadType: "media",
        metadata: {
          contentType: 'image/png',
          metadata: {
            firebaseStorageDownloadTokens: uuid
          }
        }
      })
      .then((data) => {

          let file = data[0];

          return Promise.resolve("https://firebasestorage.googleapis.com/v0/b/" + bucket.name + "/o/" + encodeURIComponent(file.name) + "?alt=media&token=" + uuid);
      });
}

puis appelez avec

upload(localPath, remotePath).then( downloadURL => {
    console.log(downloadURL);
  });

L'important est qu'il existe un objet metadata imbriqué dans la propriété d'option metadata. Si vous définissez firebaseStorageDownloadTokens sur une valeur uuid-v4, Cloud Storage l’utilisera comme jeton d’authentification publique.

Merci beaucoup à @martemorfosis

34
Drew Beaupre

Avec les changements récents dans les fonctions object response, vous pouvez obtenir tout ce dont vous avez besoin pour "assembler" l’URL de téléchargement comme suit:

 const img_url = 'https://firebasestorage.googleapis.com/v0/b/[YOUR BUCKET]/o/'
+ encodeURIComponent(object.name)
+ '?alt=media&token='
+ object.metadata.firebaseStorageDownloadTokens;

console.log('URL',img_url);
11
Demian S

Une méthode que j'utilise avec succès consiste à définir une valeur UUID v4 sur une clé nommée firebaseStorageDownloadTokens dans les métadonnées du fichier une fois le téléchargement terminé, puis d'assembler moi-même l'URL de téléchargement en suivant la structure utilisée par Firebase pour générer ces URL, par exemple:

https://firebasestorage.googleapis.com/v0/b/[BUCKET_NAME]/o/[FILE_PATH]?alt=media&token=[THE_TOKEN_YOU_CREATED]

Je ne sais pas à quel point cette méthode est "sûre" (étant donné que Firebase pourrait changer la façon dont elle génère les URL de téléchargement à l'avenir), mais elle est facile à implémenter.

10
martemorfosis

Je suggère d'utiliser l'option predefinedAcl: 'publicRead' lors du téléchargement d'un fichier avec Cloud Storage NodeJS 1.6.x ou +:

const options = {
    destination: yourFileDestination,
    predefinedAcl: 'publicRead'
};

bucket.upload(attachment, options);

Ensuite, obtenir l'URL publique est aussi simple que:

bucket.upload(attachment, options).then(result => {
    const file = result[0];
    return file.getMetadata();
}).then(results => {
    const metadata = results[0];
    console.log('metadata=', metadata.mediaLink);
}).catch(error => {
    console.error(error);
});
9
Laurent

Si vous travaillez sur un projet Firebase, vous pouvez créer des URL signées dans une fonction Cloud sans inclure d'autres bibliothèques ni télécharger un fichier d'informations d'identification. Vous devez simplement activer l'API IAM et ajouter un rôle à votre compte de service existant (voir ci-dessous).

Initialisez la bibliothèque admin et obtenez une référence de fichier comme vous le feriez normalement:

import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'

admin.initializeApp(functions.config().firebase)

const myFile = admin.storage().bucket().file('path/to/my/file')

Vous générez ensuite une URL signée avec

myFile.getSignedUrl({action: 'read', expires: someDateObj}).then(urls => {
    const signedUrl = urls[0]
})

Assurez-vous que votre compte de service Firebase dispose des autorisations suffisantes pour exécuter cette opération 

  1. Accédez à la console Google API et activez l’API IAM ( https://console.developers.google.com/apis/api/iam.googleapis.com/overview ).
  2. Toujours dans la console de l'API, accédez au menu principal, "IAM & admin" -> "IAM"
  3. Cliquez sur Modifier pour le rôle "Compte de service par défaut App Engine".
  4. Cliquez sur "Ajouter un autre rôle" et ajoutez celui appelé "Créateur de jetons de compte de service".
  5. Enregistrez et attendez une minute pour que les modifications se propagent

Avec une configuration Vanilla Firebase, la première fois que vous exécutez le code ci-dessus, vous obtenez une erreur l'API IAM (Identity and Access Management) n'a pas encore été utilisée dans le projet XXXXXX ou est désactivée.. Si vous suivez le lien dans le message d'erreur et activez l'API IAM, vous obtiendrez une autre erreur: L'autorisation iam.serviceAccounts.signBlob est requise pour effectuer cette opération sur le compte de service my-service-account. L'ajout du rôle Créateur de jetons résout ce deuxième problème d'autorisation.

9
SMX

Pour ceux qui se demandent où devrait aller le fichier serviceAccountKey.json du SDK administrateur Firebase. Il suffit de le placer dans le dossier des fonctions et de le déployer comme d’habitude.

Je ne comprends toujours pas pourquoi nous ne pouvons pas simplement obtenir l'URL de téléchargement à partir des métadonnées comme nous le faisons dans le SDK Javascript. Générer une URL qui va éventuellement expirer et la sauvegarder dans la base de données n'est pas souhaitable.

7
Clinton

Désolé mais je ne peux pas poster de commentaire sur votre question ci-dessus à cause d'une réputation manquante. Je l'inclurai donc dans cette réponse.

Faites comme indiqué ci-dessus en générant une URL signée, mais au lieu d'utiliser le service-account.json, je pense que vous devez utiliser le serviceAccountKey.json que vous pouvez générer (remplacez YOURPROJECTID en conséquence).

https://console.firebase.google.com/project/YOURPROJECTID/settings/serviceaccounts/adminsdk

Exemple:

const gcs = require('@google-cloud/storage')({keyFilename: 'serviceAccountKey.json'});
// ...
const bucket = gcs.bucket(bucket);
// ...
return bucket.upload(tempLocalFile, {
        destination: filePath,
        metadata: {
          contentType: 'image/jpeg'
        }
      })
      .then((data) => {
        let file = data[0]
        file.getSignedUrl({
          action: 'read',
          expires: '03-17-2025'
        }, function(err, url) {
          if (err) {
            console.error(err);
            return;
          }

          // handle url 
        })
6
NiVeK92

Cela fonctionne si vous avez juste besoin d'un fichier public avec une simple URL. Notez que cela peut remplacer vos règles de stockage Firebase.

bucket.upload(file, function(err, file) {
    if (!err) {
      //Make the file public
      file.acl.add({
      entity: 'allUsers',
      role: gcs.acl.READER_ROLE
      }, function(err, aclObject) {
          if (!err) {
              var URL = "https://storage.googleapis.com/[your bucket name]/" + file.id;
              console.log(URL);
          } else {
              console.log("Failed to set permissions: " + err);
          }
      });  
    } else {
        console.log("Upload failed: " + err);
    }
});
2
Dakine

J'avais le même problème, cependant, je regardais le code de l'exemple de fonction de base de feu au lieu du fichier README. Et les réponses sur ce fil n'ont pas aidé non plus ... 

Vous pouvez éviter de transmettre le fichier de configuration en procédant comme suit: 

Accédez à la console Cloud de votre projet> IAM & admin> IAM , Trouvez l'application Compte de service par défaut du moteur et ajoutez le jeton de compte de service Rôle de créateur pour ce membre. Cela permettra à votre application de créer signé URL publiques des images.

source: Fonction de génération automatique de vignettes README

Votre rôle pour le moteur d'application devrait ressembler à ceci:

 Cloud Console

1
TheFullResolution

Je ne peux pas commenter la réponse de James Daniels, mais je pense que c'est très important à lire.

Donner une URL signée Comme dans de nombreux cas, il semble joli mauvais et possible Dangereux . Selon la documentation de Firebase, l'URL signée expire après un certain temps, donc l'ajout de votre base de données à conduire à une URL vide après un certain délai

Il se peut que la documentation y soit mal comprise et que l’URL signée n’expire pas, ce qui poserait des problèmes de sécurité en conséquence… .. La clé semble être la même pour tous les fichiers téléchargés. Cela signifie qu'une fois que vous avez l'URL d'un fichier, quelqu'un peut facilement accéder à des fichiers auxquels il n'est pas supposé accéder, simplement en connaissant leurs noms.

Si je ne comprenais pas cela, alors je voudrais être corrigé . Sinon quelqu'un devrait probablement mettre à jour la solution ci-dessus nommée .

1
Renji

Pour ceux qui utilisent Firebase SDK andadmin.initializeApp:

1 - Générer une clé privée et placer dans le dossier/functions.

2 - Configurez votre code comme suit:

const serviceAccount = require('../../serviceAccountKey.json');
try { admin.initializeApp(Object.assign(functions.config().firebase, { credential: admin.credential.cert(serviceAccount) })); } catch (e) {}

Documentation

Le try/catch est parce que j'utilise un index.js qui importe d'autres fichiers et crée une fonction pour chaque fichier. Si vous utilisez un seul fichier index.js avec toutes les fonctions, vous devriez pouvoir utiliser admin.initializeApp(Object.assign(functions.config().firebase, { credential: admin.credential.cert(serviceAccount) }));.

1
Allan Poppe

Cette réponse résumera les options permettant d'obtenir l'URL de téléchargement lors du téléchargement d'un fichier sur Google/Firebase Cloud Storage. Il existe trois types d’URL de téléchargement:

  1. uRL de téléchargement signées, temporaires et dotées de fonctions de sécurité
  2. uRL de téléchargement de jetons, persistantes et dotées de fonctions de sécurité
  3. uRL de téléchargement publiques persistantes et peu sécurisées

Il existe trois manières d'obtenir une URL de téléchargement de jeton. Les deux autres URL de téléchargement ne disposent que d’un seul moyen.

À partir de la console de stockage Firebase

Vous pouvez obtenir l'URL de téléchargement à partir de la console de stockage Firebase:

enter image description here

L'URL de téléchargement ressemble à ceci:

https://firebasestorage.googleapis.com/v0/b/languagetwo-cd94d.appspot.com/o/Audio%2FEnglish%2FUnited_States-OED-0%2Fabout.mp3?alt=media&token=489c48b3-23fb-4270-bd85-0a328d2808e5

La première partie est un chemin standard vers votre fichier. À la fin est le jeton. Cette URL de téléchargement est permanente, c’est-à-dire qu’elle n’expirera pas, même si vous pouvez la révoquer.

getDownloadURL () à partir du début

Documentation nous dit d'utiliser getDownloadURL():

let url = await firebase.storage().ref('Audio/English/United_States-OED-' + i +'/' + $scope.Word.word + ".mp3").getDownloadURL();

Vous obtenez la même URL de téléchargement que celle que vous pouvez obtenir à partir de votre console Firebase Storage. Cette méthode est simple mais nécessite que vous connaissiez le chemin d'accès à votre fichier, qui dans mon application représente environ 300 lignes de code, pour une structure de base de données relativement simple. Si votre base de données est complexe, ce serait un cauchemar. Et vous pouvez télécharger des fichiers à partir du système frontal, mais cela exposerait vos informations d'identification à toute personne qui télécharge votre application. Ainsi, pour la plupart des projets, vous souhaitez télécharger vos fichiers à partir de votre back-office Node ou de Google Cloud Functions, puis obtenir l'URL de téléchargement et l'enregistrer dans votre base de données avec d'autres données relatives à votre fichier.

getSignedUrl () pour les URL de téléchargement temporaires

getSignedUrl () est facile à utiliser à partir d'un back-end Nœud ou de fonctions Google Cloud:

  function oedPromise() {
    return new Promise(function(resolve, reject) {
      http.get(oedAudioURL, function(response) {
        response.pipe(file.createWriteStream(options))
        .on('error', function(error) {
          console.error(error);
          reject(error);
        })
        .on('finish', function() {
          file.getSignedUrl(config, function(err, url) {
            if (err) {
              console.error(err);
              return;
            } else {
              resolve(url);
            }
          });
        });
      });
    });
  }

Une URL de téléchargement signée ressemble à ceci:

https://storage.googleapis.com/languagetwo-cd94d.appspot.com/Audio%2FSpanish%2FLatin_America-Sofia-Female-IBM%2Faqu%C3%AD.mp3?GoogleAccessId=languagetwo-cd94d%40appspot.gserviceaccount.com&Expires=4711305600&Signature=WUmABCZIlUp6eg7dKaBFycuO%2Baz5vOGTl29Je%2BNpselq8JSl7%2BIGG1LnCl0AlrHpxVZLxhk0iiqIejj4Qa6pSMx%2FhuBfZLT2Z%2FQhIzEAoyiZFn8xy%2FrhtymjDcpbDKGZYjmWNONFezMgYekNYHi05EPMoHtiUDsP47xHm3XwW9BcbuW6DaWh2UKrCxERy6cJTJ01H9NK1wCUZSMT0%2BUeNpwTvbRwc4aIqSD3UbXSMQlFMxxWbPvf%2B8Q0nEcaAB1qMKwNhw1ofAxSSaJvUdXeLFNVxsjm2V9HX4Y7OIuWwAxtGedLhgSleOP4ErByvGQCZsoO4nljjF97veil62ilaQ%3D%3D

L'URL signée a une date d'expiration et une signature longue. Les URL signées peuvent être révoquées en masse à partir de votre panneau de configuration Google IAM en modifiant votre signature. Le problème avec les URL signées est qu'elles expirent au bout de sept jours environ. Vous pouvez définir une date d'expiration plus courte, mais une date d'expiration plus longue ne sert à rien, votre lien de téléchargement sera mort dans une semaine.

Je vais dire ici que la documentation de Google ne dit jamais que votre URL signée expirera dans une semaine. Le code de documentation a pour date d'expiration 3-17-2025, ce qui suggère que vous pouvez définir les années d'expiration dans le futur. Mon application fonctionnait parfaitement, puis s'est écrasée une semaine plus tard. Le message d'erreur indiquait que les signatures ne correspondaient pas, pas que l'URL de téléchargement avait expiré. J'ai apporté diverses modifications à mon code, et tout a fonctionné ... jusqu'à ce que tout s'écroule une semaine plus tard. Cela a duré plus d'un mois de frustration.

Rendre votre fichier publiquement disponible

Vous pouvez définir les autorisations sur votre fichier pour une lecture publique, comme expliqué dans la documentation . Cela peut être effectué à partir du navigateur Cloud Storage ou de votre serveur de nœud. Vous pouvez rendre un fichier public, un répertoire ou l'intégralité de votre base de données de stockage. Voici le code de nœud:

var webmPromise = new Promise(function(resolve, reject) {
      var options = {
        destination: ('Audio/' + longLanguage + '/' + pronunciation + '/' + Word + '.mp3'),
        predefinedAcl: 'publicRead',
        contentType: 'audio/' + audioType,
      };

      synthesizeParams.accept = 'audio/webm';
      var file = bucket.file('Audio/' + longLanguage + '/' + pronunciation + '/' + Word + '.webm');
      textToSpeech.synthesize(synthesizeParams)
      .then(function(audio) {
        audio.pipe(file.createWriteStream(options));
      })
      .then(function() {
        console.log("webm audio file written.");
        resolve();
      })
      .catch(error => console.error(error));
    });

Le résultat ressemblera à ceci dans votre navigateur Cloud Storage:

enter image description here

Tout le monde peut alors utiliser le chemin standard pour télécharger votre fichier:

https://storage.googleapis.com/languagetwo-cd94d.appspot.com/Audio/English/United_States-OED-0/system.mp3

Une autre façon de rendre un fichier public consiste à utiliser la méthode makePublic () . Je n'ai pas réussi à faire en sorte que cela fonctionne, il est difficile de bien définir les chemins d'accès aux compartiments et aux fichiers.

Une alternative intéressante consiste à utiliser Listes de contrôle d'accès . Vous pouvez rendre un fichier accessible uniquement aux utilisateurs que vous avez mis dans une liste ou utiliser authenticatedRead pour le rendre accessible à toute personne ayant ouvert une session depuis un compte Google. S'il existait une option "toute personne se connectant à mon application à l'aide de Firebase Auth", je l'utiliserais, car cela limiterait l'accès à mes utilisateurs uniquement.

Créez votre propre URL de téléchargement avec firebaseStorageDownloadTokens

Plusieurs réponses décrivent une propriété d'objet de stockage Google non documentée firebaseStorageDownloadTokens. Avec cela, vous pouvez indiquer à Storage le jeton que vous souhaitez utiliser. Vous pouvez générer un jeton avec le module uuid Node. Quatre lignes de code et vous pouvez créer votre propre URL de téléchargement, la même URL de téléchargement obtenue à partir de la console ou getDownloadURL(). Les quatre lignes de code sont:

const uuidv4 = require('uuid/v4');
const uuid = uuidv4();
metadata: { firebaseStorageDownloadTokens: uuid }
https://firebasestorage.googleapis.com/v0/b/" + bucket.name + "/o/" + encodeURIComponent('Audio/' + longLanguage + '/' + pronunciation + '/' + Word + '.webm') + "?alt=media&token=" + uuid);

Voici le code en contexte:

var webmPromise = new Promise(function(resolve, reject) {
  var options = {
    destination: ('Audio/' + longLanguage + '/' + pronunciation + '/' + Word + '.mp3'),
    contentType: 'audio/' + audioType,
    metadata: {
      metadata: {
        firebaseStorageDownloadTokens: uuid,
      }
    }
  };

      synthesizeParams.accept = 'audio/webm';
      var file = bucket.file('Audio/' + longLanguage + '/' + pronunciation + '/' + Word + '.webm');
      textToSpeech.synthesize(synthesizeParams)
      .then(function(audio) {
        audio.pipe(file.createWriteStream(options));
      })
      .then(function() {
        resolve("https://firebasestorage.googleapis.com/v0/b/" + bucket.name + "/o/" + encodeURIComponent('Audio/' + longLanguage + '/' + pronunciation + '/' + Word + '.webm') + "?alt=media&token=" + uuid);
      })
      .catch(error => console.error(error));
});

Ce n'est pas une faute de frappe - vous devez imbriquer firebaseStorageDownloadTokens dans une double couche de metadata:!

Doug Stevenson a souligné que firebaseStorageDownloadTokens n'est pas une fonctionnalité officielle de Google Cloud Storage. Vous ne le trouverez dans aucune documentation Google, et rien ne promet que ce sera dans la version future de @google-cloud. J'aime firebaseStorageDownloadTokens car c'est le seul moyen d'obtenir ce que je veux, mais il y a une "odeur" dont l'utilisation n'est pas sûre.

Pourquoi pas de getDownloadURL () depuis le noeud?

Comme @Clinton l'a écrit, je ne comprends pas pourquoi Google ne fait pas d'une file.getDownloadURL() une méthode dans @google-cloud/storage (c'est-à-dire votre nœud principal Nœud). Je souhaite télécharger un fichier à partir de Google Cloud Functions et obtenir l'URL de téléchargement du jeton. La médiocre documentation de Google sur ce sujet aggrave encore cette gêne.

1

C’est ce que j’utilise actuellement, c’est simple et cela fonctionne parfaitement. 

Vous n'avez rien à faire avec Google Cloud. Cela fonctionne avec Firebase ..

// Save the base64 to storage.
const file = admin.storage().bucket('url found on the storage part of firebase').file(`profile_photos/${uid}`);
await file.save(base64Image, {
    metadata: {
      contentType: 'image/jpeg',
    },
    predefinedAcl: 'publicRead'
});
const metaData = await file.getMetadata()
const url = metaData[0].mediaLinkenter
0
Oliver Dixon

Si vous utilisez la valeur prédéfinie des listes de contrôle d'accès de 'publicRead', vous pouvez télécharger le fichier et y accéder avec une structure d'URL très simple:

// Upload to GCS
const opts: UploadOptions = {
  gzip: true,
  destination: dest, // 'someFolder/image.jpg'
  predefinedAcl: 'publicRead',
  public: true
};
return bucket.upload(imagePath, opts);

Vous pouvez ensuite construire l'URL comme ceci:

const storageRoot = 'https://storage.googleapis.com/';
const bucketName = 'myapp.appspot.com/'; // CHANGE TO YOUR BUCKET NAME
const downloadUrl = storageRoot + bucketName + encodeURIComponent(dest);
0
inorganik

C'est le meilleur que je suis venu. C'est redondant, mais c'est la seule solution raisonnable qui a fonctionné pour moi.

await bucket.upload(localFilePath, {destination: uploadPath, public: true});
const f = await bucket.file(uploadPath)
const meta = await f.getMetadata()
console.log(meta[0].mediaLink)
0
Tibor Udvari

Depuis firebase 6.0.0, je pouvais accéder au stockage directement avec l’administrateur comme ceci: 

const bucket = admin.storage().bucket();

Je n'ai donc pas eu besoin d'ajouter un compte de service. Puis définir l’UUID comme indiqué ci-dessus fonctionnait pour obtenir l’url de la base de feu.

0
NickJ