Obtenir l'URL de téléchargement à partir du fichier téléchargé avec Cloud Functions pour Firebase

125

Après avoir téléchargé un fichier dans Firebase Storage avec des fonctions pour 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 contient de nombreux paramètres. Même un nommé mediaLink. Cependant, si j'essaye d'accéder à ce lien, j'obtiens cette erreur:

Les utilisateurs anonymes n'ont pas accès à storage.objects.get à l'objet ...

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

Je vous remercie

Valentin
la source
Voir aussi cet article qui reconstruit l'URL à partir des données disponibles dans la fonction.
Kato

Réponses:

134

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

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 devrez initialiser @google-cloud/storageavec les informations d' identification de votre compte de service, car les informations d'identification par défaut de l'application ne seront pas suffisantes.

MISE À JOUR : Le SDK Cloud Storage est désormais accessible via le SDK Firebase Admin, qui agit comme un wrapper autour de @ google-cloud / storage. La seule façon de procéder est que vous:

  1. Lancez le SDK avec un compte de service spécial, généralement via une seconde instance non par défaut.
  2. Ou, sans compte de service, en accordant au compte de service App Engine par défaut l'autorisation "signBlob".
James Daniels
la source
74
Cela est étrange. Nous pouvons facilement obtenir l'URL de téléchargement à partir d'une référence de stockage lors de l'utilisation du SDK Firebase Android, iOS et Web. Pourquoi n'est-ce pas aussi facile d'être administrateur? PS: Où puis-je trouver le 'service-account.json' nécessaire pour initialiser gcs?
Valentin
2
En effet, admin-sdk ne dispose d'aucun ajout Cloud Storage. Vous pouvez obtenir votre compte de service admin-sdk json ici console.firebase.google.com/project/_/settings/serviceaccounts/…
James Daniels
18
L'URL générée avec cette méthode est ridiculement longue. L'URL générée par la méthode proposée par @martemorfosis est bien meilleure. Y a-t-il une fonction qui produit cette URL? C'est ce que j'enregistre dans la base de données pour une utilisation future lorsque j'utilise firebase-sdk. Une méthode miroir doit exister dans le domaine Functions.
Bogac
3
Comment pouvons-nous télécharger le service-account.json avec les fonctions déployées? J'ai essayé de le placer dans le dossier des fonctions et de le référencer dans le champ de fichier dans package.json mais il ne se déploie pas. Je vous remercie.
David Aroesti
2
Sommes-nous obligés d'ajouter actionet expires?
Chad Bingham
83

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'essentiel ici est qu'il y a un metadataobjet imbriqué dans la metadatapropriété option. La définition firebaseStorageDownloadTokensd'une valeur uuid-v4 indiquera à Cloud Storage de l'utiliser comme jeton d'authentification public.

Un grand merci à @martemorfosis

Drew Beaupre
la source
Comment obtenir un jeton UUID valide pour un fichier déjà téléchargé sur le stockage? La génération d'un UUID aléatoire n'a pas aidé. Des pointeurs?
DerFaizio
3
J'ai trouvé la réponse dans le post @martemorfosis. L'UUID peut être récupéré à partir de l'objet.metadata exports.uploadProfilePic = functions.storage.object (). OnChange (event => {const object = event.data; // L'objet de stockage. Const uuid = object.metadata.firebaseStorageDownloadTokens; // ...
DerFaizio
Merci pour l'exemple du seau! J'essayais différentes combinaisons pour les méthodes de seau et de fichier pendant près d'une heure :)
JCarlosR
1
Merci pour votre réponse! Dans mon cas, je téléchargeais avec bucket.file (fileName) .createWriteStream qui ne renvoie pas de données une fois le téléchargement terminé, j'ai donc utilisé encodeURIComponent (fileName) au lieu de encodeURIComponent (file.name).
Stanislau Buzunko
2
Cela devrait être la bonne réponse. Il en résulte une URL similaire à celle générée par les SDK Firebase (@DevMike), et je parie que c'est exactement ce qu'ils font dans les coulisses.
Samuel E.
64

Cette réponse résume les options pour 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 fonctionnalités de sécurité
  2. URL de téléchargement de jetons, persistantes et dotées de fonctionnalités de sécurité
  3. URL de téléchargement publiques, qui sont persistantes et manquent de sécurité

Il existe trois façons d'obtenir une URL de téléchargement de jeton. Les deux autres URL de téléchargement n'ont qu'un seul moyen de les obtenir.

Depuis la console de stockage Firebase

Vous pouvez obtenir l'URL de téléchargement depuis la console Firebase Storage:

entrez la description de l'image ici

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 se trouve le jeton. Cette URL de téléchargement est permanente, c'est-à-dire qu'elle n'expirera pas, bien que vous puissiez la révoquer.

getDownloadURL () depuis le frontal

La documentation nous dit d'utiliser getDownloadURL():

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

Cela obtient la même URL de téléchargement que celle que vous pouvez obtenir depuis 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 compte 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 depuis le frontal, mais cela exposerait vos informations d'identification à quiconque télécharge votre application. Ainsi, pour la plupart des projets, vous voudrez télécharger vos fichiers depuis votre back-end Node ou 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 sur votre fichier.

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

getSignedUrl () est facile à utiliser à partir d'un back-end Node ou de Google Cloud Functions:

  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. La documentation de la ligne de commande gsutil signurl -d indique que les URL signées sont temporaires: l'expiration par défaut est d'une heure et l'expiration maximale est de sept jours.

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

Rendre votre fichier accessible au public

Vous pouvez définir les autorisations sur votre fichier en lecture publique, comme expliqué dans la documentation . Cela peut être fait à partir du navigateur Cloud Storage ou de votre serveur Node. Vous pouvez rendre un fichier public ou un répertoire ou l'ensemble de votre base de données de stockage. Voici le code du 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:

entrez la description de l'image ici

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 est d'utiliser la méthode makePublic () . Je n'ai pas été en mesure de faire fonctionner cela, il est difficile d'obtenir le bon chemin de seau et de fichier.

Une alternative intéressante consiste à utiliser les listes de contrôle d'accès . Vous pouvez rendre un fichier disponible uniquement pour les utilisateurs que vous avez mis sur une liste, ou utiliser authenticatedReadpour rendre le fichier disponible à toute personne qui est connectée à partir d'un compte Google. S'il y avait une option "toute personne qui s'est connectée à mon application en utilisant 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 Google Storage 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 uuidmodule 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 que vous obtenez depuis 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 firebaseStorageDownloadTokensen double couche de metadata:!

Doug Stevenson a souligné qu'il firebaseStorageDownloadTokensne s'agissait pas d'une fonctionnalité officielle de Google Cloud Storage. Vous ne le trouverez dans aucune documentation Google et il n'y a aucune promesse qu'il le sera dans la future version de @google-cloud. J'aime firebaseStorageDownloadTokensparce que c'est le seul moyen d'obtenir ce que je veux, mais il a une "odeur" qui n'est pas sûre à utiliser.

Pourquoi pas getDownloadURL () de Node?

Comme @Clinton l'a écrit, Google devrait créer une file.getDownloadURL()méthode dans @google-cloud/storage(c'est- à -dire, votre back-end Node). Je souhaite importer un fichier depuis Google Cloud Functions et obtenir l'URL de téléchargement du jeton.

Thomas David Kehoe
la source
10
J'ai créé un numéro @google-cloud/storagepour cela, n'hésitez pas à le +1;) github.com/googleapis/nodejs-storage/issues/697
Théo Champion
1
dernier lien makePublic () .
galki
1
Il semble que firebaseStorageDownloadTokenscela ne fonctionne plus.
Mason
1
La réponse acceptée suggère qu'il n'est pas possible d'obtenir une URL de téléchargement persistante qui n'expire pas et qui n'est pas correcte. Le détail de votre réponse est excellent et doit être marqué comme la bonne réponse. Je vous remercie.
DevMike
2
@thomas merci pour le superbe résumé! Vous avez mentionné qu'il existe 3 façons d'obtenir une URL de téléchargement de jeton persistant, mais vous n'en avez partagé que 2: (a) à partir de la console de stockage Firebase et (b) à getDownloadURL () à partir du front-end. Je me demande quelle est la troisième voie?
czphilip le
23

Avec les changements récents dans la réponse de l' objet des fonctions, 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);
Demian S
la source
2
Faites-vous référence à la réponse d'objet de bucket.file().upload()? Je ne reçois aucune propriété de métadonnées dans les données de réponse et je ne sais pas comment les obtenir firebaseStorageDownloadTokens.
Dygerati
aussi [VOTRE SEAU] est bucket.name, vous n'avez pas à le coder en dur ou à utiliser un var local supplémentaire
Călin Darie
4
Le problème avec cette solution est que l'URL du service est codée en dur. Si Firebase / Google le modifie, il peut se casser. L'utilisation de la metadata.mediaLinkpropriété empêche un tel problème.
Laurent
2
Ce n'est pas un cas pris en charge pour créer une URL comme celle-ci. Cela peut fonctionner aujourd'hui, mais pourrait se rompre à l'avenir. Vous ne devez utiliser les API fournies que pour générer une URL de téléchargement appropriée.
Doug Stevenson
1
S'appuyer sur une URL codée en dur qui peut changer est un mauvais choix.
Laurent
23

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 ou télécharger un fichier d'informations d'identification. Il vous suffit d'activer l'API IAM et d'ajouter un rôle à votre compte de service existant (voir ci-dessous).

Initialisez la bibliothèque d'administration 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 l'exécuter

  1. Accédez à la console API Google et activez l'API IAM ( https://console.developers.google.com/apis/api/iam.googleapis.com/overview )
  2. Toujours dans la console API, allez dans le 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 que les modifications se propagent

Avec une configuration Firebase vanilla, la première fois que vous exécutez le code ci-dessus, vous obtiendrez une erreur L'API de gestion des identités et des accès (IAM) n'a pas été utilisée dans le projet XXXXXX auparavant ou elle est désactivée. . Si vous suivez le lien dans le message d'erreur et activez l'API IAM, vous obtiendrez une autre erreur: Permission iam.serviceAccounts.signBlob est nécessaire pour effectuer cette opération sur le compte de service my-service-account . L'ajout du rôle de créateur de jetons résout ce deuxième problème d'autorisation.

SMX
la source
J'étais sur le point de laisser une réponse avec fondamentalement les mêmes détails que j'ai FINALEMENT compris à la dure - j'aurais bien aimé avoir lu les solutions aussi loin plus tôt: / Cela a fonctionné pour moi à partir du 12/12/18. Merci pour les instructions détaillées, très utiles pour nous débutants !!
Kat
2
Mon signurl expire dans 2 semaines mais j'utilise admin.initializeApp () sans clé, est-ce le problème? J'ai défini le compte de service par défaut de l'application App Engine sur "propriétaire" et l'agent de service Cloud Functions. Je viens de supprimer le "propriétaire" pour le moment et j'ai ajouté "Créateur de jetons de compte de service"
Amit Bravo le
2
Les URL signées expirent dans 7 jours. Vous pouvez définir une date d'expiration plus courte mais pas plus longue.
Thomas David Kehoe
Comment actualiser l'URL si elle expire?
Manoj MM
comment actualiser l'url pour la définir sur une durée plus longue?
Saifallak
17

Une méthode que j'utilise avec succès consiste à définir une valeur UUID v4 sur une clé nommée firebaseStorageDownloadTokensdans les métadonnées du fichier une fois le téléchargement terminé, puis à assembler l'URL de téléchargement moi-même 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 utiliser cette méthode est «sûre» (étant donné que Firebase pourrait changer la façon dont il génère les URL de téléchargement à l'avenir) mais elle est facile à mettre en œuvre.

martémorfose
la source
1
Avez-vous un exemple où vous définissez la valeur uuid?
Drew Beaupre
1
J'ai la même question que Drew, où définissez-vous les métadonnées? J'ai essayé de définir pendant que la fonction bucket.upload ne fonctionnait pas.
Vysakh Sreenivasan
1
Vysakh, j'ai publié une réponse complète avec un exemple. J'espère que cela vous aide.
Drew Beaupre
Où / comment créez-vous le jeton?
CodyBugstein
3
Je ne considérerais pas cette technique comme «sûre», car les URL de téléchargement sont censées être opaques, dont les composants ne doivent pas être décomposés ou assemblés.
Doug Stevenson
16

Pour ceux qui se demandent où doit aller le fichier serviceAccountKey.json du SDK Admin Firebase. Placez-le simplement dans le dossier des fonctions et déployez-le comme d'habitude.

Je ne sais 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. Il n'est pas souhaitable de générer une URL qui expirera éventuellement et de l'enregistrer dans la base de données.

Clinton
la source
15

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);
});
Laurent
la source
2
Cela semble en fait fonctionner. Le seul inconvénient que je vois jusqu'à présent est que si vous appuyez sur l'image dans la barre d'URL d'un navigateur, il téléchargera l'image au lieu de la visualiser en ligne.
Michael Giovanni Pumo
file.getMetadata () a fait l'affaire pour moi après avoir utilisé la méthode save () sur la référence du fichier. Utilisation dans NodeJS avec firebase-admin sdk.
Pascal Lamers
n'a pas fonctionné, je reçois un appelant anonyme n'a pas accès à storage.objects.get your_app / image.jpg
Manoj MM
9

Désolé, mais je ne peux pas publier de commentaire sur votre question ci-dessus en raison d'une réputation manquante, je vais donc l'inclure 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 
        })
NiVeK92
la source
9

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 il l'a fait semble dans de nombreux cas assez mauvais et possible dangereux . Selon la documentation de Firebase, l'URL signée expire après un certain temps, donc l'ajouter à votre base de données conduira à 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 entraînerait des problèmes de sécurité. La clé semble être la même pour chaque fichier téléchargé. Cela signifie qu'une fois que vous avez obtenu l'URL d'un fichier, quelqu'un peut facilement accéder aux fichiers auxquels il n'est pas censé accéder, simplement en connaissant leurs noms.

Si je n'ai pas compris cela, je voudrais être corrigé. Sinon, quelqu'un devrait probablement mettre à jour la solution nommée ci-dessus. Si je peux me tromper

Renji
la source
7

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

Vous n'avez rien à faire avec Google Cloud. Cela fonctionne directement 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].mediaLink

EDIT: même exemple, mais avec téléchargement:

await bucket.upload(fromFilePath, {destination: toFilePath});
file = bucket.file(toFilePath);
metaData = await file.getMetadata()
const trimUrl = metaData[0].mediaLink

mettre à jour:

pas besoin de faire deux appels différents dans la méthode de téléchargement pour obtenir les métadonnées:

let file = await bucket.upload(fromFilePath, {destination: toFilePath});
const trimUrl = file[0].metaData.mediaLink
Oliver Dixon
la source
1
Comment l'utiliseriez-vous avec un fichier qui n'est pas encodé en base64?
Tibor Udvari
1
Ce n'est pas MediaLinkenter, c'est juste MediaLink
l2aelba
1
Je ne trouve pas mediaLink i.stack.imgur.com/B4Fw5.png
sarah le
@Sarah J'ai écrit ceci en utilisant du tapuscrit, je ne sais pas s'il y a un remplacement de module.
Oliver Dixon
3

J'ai eu le même problème, cependant, je regardais le code de l'exemple de fonction firebase au lieu du 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 à Cloud Console> IAM et administration> IAM de votre projet , recherchez le compte de service par défaut App Engine et ajoutez le rôle de créateur de jeton de compte de service à ce membre. Cela permettra à votre application de créer des URL publiques signées vers les images.

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

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

Cloud Console

TheFullRésolution
la source
3

Si vous utilisez la valeur de listes de contrôle d'accès prédéfinie 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);
inorganik
la source
2

Cela fonctionne si vous avez juste besoin d'un fichier public avec une URL simple. 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);
    }
});
Dakine
la source
1

Pour ceux qui utilisent le SDK Firebase et admin.initializeApp:

1 - Générez une clé privée et placez-la 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 dû au fait 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 être d'accord avec admin.initializeApp(Object.assign(functions.config().firebase, { credential: admin.credential.cert(serviceAccount) }));.

Allan Poppe
la source
pour moi c'était '../serviceaccountkey.json' mais merci pour la tête pour utiliser le ../
robert king
1

Depuis Firebase 6.0.0, j'ai pu 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. Ensuite, la définition de l'UUID comme indiqué ci-dessus a fonctionné pour obtenir l'URL de la base de feu.

NickJ
la source
1

C'est le meilleur que je connaisse. 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)
Tibor Udvari
la source
1

Sans signedURL()utilisermakePublic()

const functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp()
var bucket = admin.storage().bucket();

// --- [Above] for admin related operations, [Below] for making a public url from a GCS uploaded object

const { Storage } = require('@google-cloud/storage');
const storage = new Storage();

exports.testDlUrl = functions.storage.object().onFinalize(async (objMetadata) => {
    console.log('bucket, file', objMetadata.bucket + ' ' + objMetadata.name.split('/').pop()); // assuming file is in folder
    return storage.bucket(objMetadata.bucket).file(objMetadata.name).makePublic().then(function (data) {
        return admin.firestore().collection('publicUrl').doc().set({ publicUrl: 'https://storage.googleapis.com/' + objMetadata.bucket + '/' + objMetadata.name }).then(writeResult => {
            return console.log('publicUrl', writeResult);
        });
    });
});
ersin-ertan
la source
1

réponse par https://stackoverflow.com/users/269447/laurent fonctionne mieux

const uploadOptions: UploadOptions = {
    public: true
};

const bucket = admin.storage().bucket();
[ffile] = await bucket.upload(oPath, uploadOptions);
ffile.metadata.mediaLink // this is what you need
Jasdeep Singh
la source
0

Si vous obtenez une erreur:

Fonctions Google Cloud: require (…) n'est pas une fonction

essaye ça:

const {Storage} = require('@google-cloud/storage');
const storage = new Storage({keyFilename: 'service-account-key.json'});
const bucket = storage.bucket(object.bucket);
const file = bucket.file(filePath);
.....
Chèvre sauvage
la source
0

Je poste déjà mes ans ... dans l'URL ci-dessous Où vous pouvez obtenir le code complet avec la solution

Comment importer une image (chaîne) encodée en base64 directement dans un bucket Google Cloud Storage à l'aide de Node.js?

const uuidv4 = require('uuid/v4');
const uuid = uuidv4();

    const os = require('os')
    const path = require('path')
    const cors = require('cors')({ origin: true })
    const Busboy = require('busboy')
    const fs = require('fs')
    var admin = require("firebase-admin");


    var serviceAccount = {
        "type": "service_account",
        "project_id": "xxxxxx",
        "private_key_id": "xxxxxx",
        "private_key": "-----BEGIN PRIVATE KEY-----\jr5x+4AvctKLonBafg\nElTg3Cj7pAEbUfIO9I44zZ8=\n-----END PRIVATE KEY-----\n",
        "client_email": "[email protected]",
        "client_id": "xxxxxxxx",
        "auth_uri": "https://accounts.google.com/o/oauth2/auth",
        "token_uri": "https://oauth2.googleapis.com/token",
        "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
        "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-5rmdm%40xxxxx.iam.gserviceaccount.com"
      }

    admin.initializeApp({
        credential: admin.credential.cert(serviceAccount),
        storageBucket: "xxxxx-xxxx" // use your storage bucket name
    });


    const app = express();
    app.use(bodyParser.urlencoded({ extended: false }));
    app.use(bodyParser.json());
app.post('/uploadFile', (req, response) => {
    response.set('Access-Control-Allow-Origin', '*');
    const busboy = new Busboy({ headers: req.headers })
    let uploadData = null
    busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
        const filepath = path.join(os.tmpdir(), filename)
        uploadData = { file: filepath, type: mimetype }
        console.log("-------------->>",filepath)
        file.pipe(fs.createWriteStream(filepath))
      })

      busboy.on('finish', () => {
        const bucket = admin.storage().bucket();
        bucket.upload(uploadData.file, {
            uploadType: 'media',
            metadata: {
              metadata: { firebaseStorageDownloadTokens: uuid,
                contentType: uploadData.type,
              },
            },
          })

          .catch(err => {
            res.status(500).json({
              error: err,
            })
          })
      })
      busboy.end(req.rawBody)
   });




exports.widgets = functions.https.onRequest(app);
Rawan-25
la source