Ottieni l'URL di download dal file caricato con Cloud Functions per Firebase


125

Dopo aver caricato un file in Firebase Storage con funzioni per Firebase, vorrei ottenere l'URL di download del file.

Ho questo :

...

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

        // Get the download url of file

    });

Il file oggetto ha molti parametri. Anche uno di nome mediaLink. Tuttavia, se provo ad accedere a questo collegamento, ottengo questo errore:

Gli utenti anonimi non hanno accesso a storage.objects.get all'oggetto ...

Qualcuno può dirmi come ottenere l'Url di download pubblico?

Grazie


Vedi anche questo post che ricostruisce l'URL dai dati disponibili nella funzione.
Kato

Risposte:


134

Dovrai generare un URL firmato utilizzando getSignedURL tramite il modulo NPM @ google-cloud / storage .

Esempio:

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

Sarà necessario inizializzare @google-cloud/storagecon le credenziali dell'account di servizio poiché le credenziali predefinite dell'applicazione non saranno sufficienti.

AGGIORNAMENTO : ora è possibile accedere a Cloud Storage SDK tramite Firebase Admin SDK, che funge da wrapper attorno a @ google-cloud / storage. L'unico modo per farlo è se:

  1. Iniziare l'SDK con un account di servizio speciale, in genere tramite una seconda istanza non predefinita.
  2. Oppure, senza un account di servizio, concedendo all'account di servizio predefinito di App Engine l'autorizzazione "signBlob".

74
Questo è strano. Possiamo facilmente ottenere l'URL di download da un riferimento di archiviazione quando si utilizza Firebase Android, iOS e Web SDK. Perché non è così facile quando si è in amministrazione? PS: dove posso trovare il 'service-account.json' necessario per inizializzare gcs?
Valentin

2
Questo perché admin-sdk non ha aggiunte di Cloud Storage. Puoi ottenere il tuo account di servizio admin-sdk json qui console.firebase.google.com/project/_/settings/serviceaccounts/…
James Daniels

18
L'URL generato con questo metodo è incredibilmente lungo. L'URL generato dal metodo proposto da @martemorfosis è molto migliore. C'è qualche funzione che produce quell'URL? Questo è ciò che salvo nel database per un utilizzo futuro quando utilizzo firebase-sdk. Un metodo mirror deve esistere nel dominio delle funzioni.
Bogac

3
Come possiamo caricare il servizio-account.json lungo le funzioni distribuite? Ho provato a posizionarlo nella cartella delle funzioni ea fare riferimento ad esso nel campo del file in package.json ma non viene distribuito. Grazie.
David Aroesti

2
Dobbiamo aggiungere actione expires?
Chad Bingham

83

Ecco un esempio su come specificare il token di download durante il caricamento:

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

quindi chiama con

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

La cosa fondamentale qui è che c'è un metadataoggetto annidato all'interno della metadataproprietà option. L'impostazione firebaseStorageDownloadTokenssu un valore uuid-v4 indicherà a Cloud Storage di utilizzarlo come token di autorizzazione pubblico.

Mille grazie a @martemorfosis


Come ottengo un token UUID valido per un file che è già caricato su Storage? La generazione di UUID casuale non ha aiutato. Qualche suggerimento?
DerFaizio

3
Ho trovato la risposta nel post di @martemorfosis. L'UUID può essere recuperato da object.metadata exports.uploadProfilePic = functions.storage.object (). OnChange (event => {const object = event.data; // The Storage object. Const uuid = object.metadata.firebaseStorageDownloadTokens; // ...
DerFaizio

Grazie per l'esempio del secchio! Ho provato diverse combinazioni per il bucket e i metodi di file per quasi 1 ora :)
JCarlosR

1
Grazie per la tua risposta! Nel mio caso, stavo caricando con bucket.file (fileName) .createWriteStream che non restituisce dati al termine del caricamento, di conseguenza, ho utilizzato encodeURIComponent (fileName) invece di encodeURIComponent (file.name).
Stanislau Buzunko

2
Questa dovrebbe essere la risposta giusta. Risulta in un URL simile a quello generato dagli SDK Firebase (@DevMike) e scommetto che è esattamente quello che fanno dietro le quinte.
Samuel E.

64

Questa risposta riepilogherà le opzioni per ottenere l'URL di download durante il caricamento di un file su Google / Firebase Cloud Storage. Esistono tre tipi di URL di download:

  1. URL di download firmati, che sono temporanei e hanno funzioni di sicurezza
  2. URL di download di token, che sono persistenti e dispongono di funzionalità di sicurezza
  3. URL di download pubblici, persistenti e privi di sicurezza

Esistono tre modi per ottenere un URL di download del token. Gli altri due URL di download hanno un solo modo per ottenerli.

Dalla Firebase Storage Console

Puoi ottenere l'URL di download dalla console di Firebase Storage:

inserisci qui la descrizione dell'immagine

L'URL di download è simile a questo:

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 prima parte è un percorso standard del file. Alla fine c'è il gettone. Questo URL di download è permanente, ovvero non scadrà, sebbene tu possa revocarlo.

getDownloadURL () dal front-end

La documentazione ci dice di usare getDownloadURL():

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

Ottiene lo stesso URL di download che puoi ottenere dalla tua console di archiviazione Firebase. Questo metodo è semplice ma richiede che tu conosca il percorso del tuo file, che nella mia app è di circa 300 righe di codice, per una struttura di database relativamente semplice. Se il tuo database è complesso, questo sarebbe un incubo. E potresti caricare file dal front-end, ma questo esporrebbe le tue credenziali a chiunque scarichi la tua app. Quindi, per la maggior parte dei progetti, ti consigliamo di caricare i tuoi file dal back-end di Node o Google Cloud Functions, quindi ottenere l'URL di download e salvarlo nel database insieme ad altri dati sul file.

getSignedUrl () per gli URL di download temporanei

getSignedUrl () è facile da usare da un back-end di nodi o da 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);
            }
          });
        });
      });
    });
  }

Un URL di download firmato ha questo aspetto:

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 firmato ha una data di scadenza e una firma lunga. La documentazione per la riga di comando gsutil signurl -d dice che gli URL firmati sono temporanei: la scadenza predefinita è un'ora e la scadenza massima è sette giorni.

Ho intenzione di sbraitare qui che getSignedUrl non dice mai che il tuo URL firmato scadrà tra una settimana. Il codice della documentazione ha 3-17-2025come data di scadenza, suggerendo che è possibile impostare gli anni di scadenza in futuro. La mia app ha funzionato perfettamente e poi si è bloccata una settimana dopo. Il messaggio di errore diceva che le firme non corrispondevano, non che l'URL di download era scaduto. Ho apportato varie modifiche al mio codice e tutto ha funzionato ... fino a quando non è andato in crash una settimana dopo. Questo è andato avanti per più di un mese di frustrazione.

Rendi il tuo file disponibile pubblicamente

È possibile impostare i permessi sul file in lettura pubblica, come spiegato nella documentazione . Questo può essere fatto dal browser Cloud Storage o dal tuo server Node. È possibile rendere pubblico un file o una directory o l'intero database di archiviazione. Ecco il codice del nodo:

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

Il risultato sarà simile a questo nel tuo browser Cloud Storage:

inserisci qui la descrizione dell'immagine

Chiunque può quindi utilizzare il percorso standard per scaricare il file:

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

Un altro modo per rendere pubblico un file è usare il metodo makePublic () . Non sono riuscito a farlo funzionare, è difficile ottenere il bucket e i percorsi dei file corretti.

Un'alternativa interessante è usare gli elenchi di controllo degli accessi . Puoi rendere un file disponibile solo per gli utenti che hai inserito in un elenco o utilizzarlo authenticatedReadper rendere il file disponibile a chiunque abbia effettuato l'accesso da un account Google. Se ci fosse un'opzione "chiunque abbia effettuato l'accesso alla mia app utilizzando Firebase Auth", la userei, poiché limiterebbe l'accesso solo ai miei utenti.

Crea il tuo URL di download con firebaseStorageDownloadTokens

Diverse risposte descrivono una proprietà di un oggetto Google Storage non documentata firebaseStorageDownloadTokens. Con questo puoi dire a Storage il token che desideri utilizzare. Puoi generare un token con il uuidmodulo Node. Quattro righe di codice e puoi creare il tuo URL di download, lo stesso URL di download che ottieni dalla console o getDownloadURL(). Le quattro righe di codice sono:

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

Ecco il codice nel contesto:

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

Non è un errore di battitura: devi nidificare firebaseStorageDownloadTokensin doppi strati dimetadata: !

Doug Stevenson ha sottolineato che firebaseStorageDownloadTokensnon è una funzione ufficiale di Google Cloud Storage. Non lo troverai in nessuna documentazione di Google e non ci sono promesse che sarà nella versione futura di @google-cloud. Mi piace firebaseStorageDownloadTokensperché è l'unico modo per ottenere quello che voglio, ma ha un "odore" che non è sicuro da usare.

Perché nessun getDownloadURL () da Node?

Come ha scritto @Clinton, Google dovrebbe creare un file.getDownloadURL()metodo in @google-cloud/storage(cioè, il tuo back-end Node). Voglio caricare un file da Google Cloud Functions e ottenere l'URL di download del token.


10
Ho creato un problema @google-cloud/storageper questo, sentiti libero di fare +1;) github.com/googleapis/nodejs-storage/issues/697
Théo Champion

1
ultimo link makePublic () .
galki

1
Sembra firebaseStorageDownloadTokensche non funzioni più.
Mason

1
La risposta accettata suggerisce che non è possibile ottenere un URL di download persistente che non scada, il che non è corretto. Il dettaglio qui nella tua risposta è eccellente e dovrebbe essere contrassegnato come risposta corretta. Grazie.
DevMike

2
@thomas, grazie per il fantastico riepilogo! Hai menzionato che esistono 3 modi per ottenere un URL di download di token persistente ma ne hai condivisi solo 2: (a) dalla Firebase Storage Console e (b) getDownloadURL () dal front-end. Mi chiedo qual è la terza via?
czphilip

23

Con le recenti modifiche alle funzioni di risposta dell'oggetto puoi ottenere tutto ciò di cui hai bisogno per "cucire" insieme l'URL di download in questo modo:

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

2
Ti riferisci alla risposta dell'oggetto da bucket.file().upload()? Non ricevo alcuna proprietà di metadati nei dati di risposta e non sono sicuro di come ottenerli firebaseStorageDownloadTokens.
Dygerati

inoltre [YOUR BUCKET] è bucket.nameche non è necessario codificarlo o utilizzare una var locale extra
Călin Darie

4
Il problema con questa soluzione è che l'URL del servizio è hardcoded. Se Firebase / Google lo modifica, potrebbe rompersi. L'utilizzo della metadata.mediaLinkproprietà previene tale problema.
Laurent

2
Non è supportato il caso per creare un URL come questo. Può funzionare oggi, ma potrebbe rompersi in futuro. È necessario utilizzare solo le API fornite per generare un URL di download appropriato.
Doug Stevenson

1
Affidarsi a un URL hardcoded che potrebbe cambiare è una cattiva scelta.
Laurent

23

Se stai lavorando a un progetto Firebase, puoi creare URL firmati in una funzione Cloud senza includere altre librerie o scaricare un file di credenziali. Devi solo abilitare l'API IAM e aggiungere un ruolo al tuo account di servizio esistente (vedi sotto).

Inizializza la libreria dell'amministratore e ottieni un riferimento al file come faresti normalmente:

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')

Quindi generi un URL firmato con

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

Assicurati che il tuo account di servizio Firebase abbia autorizzazioni sufficienti per eseguirlo

  1. Vai alla console dell'API di Google e abilita l'API IAM ( https://console.developers.google.com/apis/api/iam.googleapis.com/overview )
  2. Sempre nella console API, vai al menu principale, "IAM e amministrazione" -> "IAM"
  3. Fai clic su Modifica per il ruolo "Account di servizio predefinito App Engine"
  4. Fai clic su "Aggiungi un altro ruolo" e aggiungi quello denominato "Creatore token account di servizio"
  5. Salva e attendi un minuto per la propagazione delle modifiche

Con una configurazione Vanilla Firebase, la prima volta che esegui il codice precedente riceverai un errore L'API Identity and Access Management (IAM) non è stata utilizzata nel progetto XXXXXX prima o è disabilitata. . Se segui il collegamento nel messaggio di errore e abiliti l'API IAM, riceverai un altro errore: è necessaria l'autorizzazione iam.serviceAccounts.signBlob per eseguire questa operazione sull'account di servizio my-service-account . L'aggiunta del ruolo Token Creator risolve questo secondo problema di autorizzazione.


Stavo per lasciare una risposta con fondamentalmente questi stessi dettagli che FINALMENTE ho capito nel modo più duro - sicuramente vorrei aver letto le soluzioni fino a questo punto prima: / Questo ha funzionato per me dal 12/12/18. Grazie per le istruzioni dettagliate, molto utili per noi principianti !!
Kat

2
Il mio signedurl scade tra 2 settimane ma sto usando admin.initializeApp () senza chiave, è questo il problema? Avevo l'account di servizio predefinito dell'app App Engine impostato su "proprietario" e agente di servizio Cloud Functions, per ora ho rimosso "proprietario" e ho aggiunto "Creatore token account di servizio"
Amit Bravo

2
Gli URL firmati scadono tra 7 giorni. È possibile impostare una data di scadenza più breve ma non più lunga.
Thomas David Kehoe

Come aggiornare l'URL se scade?
Manoj MM

come aggiornare l'URL per impostarlo su un tempo più lungo?
Saifallak

17

Un metodo che sto utilizzando con successo consiste firebaseStorageDownloadTokensnell'impostare un valore UUID v4 su una chiave denominata nei metadati del file al termine del caricamento e quindi assemblare l'URL di download da solo seguendo la struttura che Firebase utilizza per generare questi URL, ad esempio:

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

Non so quanto sia "sicuro" utilizzare questo metodo (dato che Firebase potrebbe cambiare il modo in cui genera gli URL di download in futuro) ma è facile da implementare.


1
Hai un esempio in cui imposti il ​​valore uuid?
Drew Beaupre

1
Ho la stessa domanda di Drew, dove imposti i metadati? Ho provato a impostare mentre la funzione bucket.upload, non ha funzionato.
Vysakh Sreenivasan

1
Vysakh, ho pubblicato una risposta completa con esempio. Spero che ti aiuti.
Drew Beaupre

Dove / come crei il token?
CodyBugstein

3
Non considererei questa tecnica "sicura", poiché gli URL di download sono pensati per essere opachi, i cui componenti non devono essere scomposti o assemblati.
Doug Stevenson

16

Per coloro che si chiedono dove dovrebbe andare il file ServiceAccountKey.json di Firebase Admin SDK. Basta posizionarlo nella cartella delle funzioni e distribuirlo come al solito.

Mi sconcerta ancora il motivo per cui non possiamo semplicemente ottenere l'URL di download dai metadati come facciamo nell'SDK Javascript. Non è consigliabile generare un URL che alla fine scadrà e salvarlo nel database.


15

Suggerisco di utilizzare l'opzione predefinedAcl: 'publicRead'durante il caricamento di un file con Cloud Storage NodeJS 1.6.xo +:

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

bucket.upload(attachment, options);

Quindi, ottenere l'URL pubblico è semplice come:

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

2
In effetti sembra funzionare. L'unico aspetto negativo che vedo finora è che se colpisci l'immagine nella barra degli URL di un browser, scaricherà l'immagine invece di visualizzarla in linea.
Michael Giovanni Pumo

file.getMetadata () ha fatto il trucco per me dopo aver utilizzato il metodo save () sul riferimento al file. Usandolo in NodeJS con firebase-admin sdk.
Pascal Lamers

non ha funzionato, ricevo il chiamante anonimo non dispone di storage.objects. get access to your_app / image.jpg
Manoj MM

9

Mi dispiace ma non posso pubblicare un commento alla tua domanda sopra a causa della mancanza di reputazione, quindi lo includerò in questa risposta.

Fai come indicato sopra generando un URL firmato, ma invece di utilizzare service-account.json penso che tu debba usare serviceAccountKey.json che puoi generare su (sostituire YOURPROJECTID di conseguenza)

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

Esempio:

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

9

Non posso commentare la risposta data da James Daniels, ma penso che sia molto importante da leggere.

Fornire un URL firmato Come ha fatto in molti casi sembra piuttosto brutto e possibilmente pericoloso . Secondo la documentazione di Firebase l'URL firmato scade dopo un po 'di tempo, quindi aggiungerlo al tuo database porterà a un URL vuoto dopo un certo periodo di tempo

È possibile che abbia frainteso la Documentazione e che l'URL firmato non scada, il che potrebbe causare alcuni problemi di sicurezza. La chiave sembra essere la stessa per ogni file caricato. Ciò significa che una volta ottenuto l'URL di un file, qualcuno potrebbe facilmente accedere ai file a cui non è autorizzato ad accedere, solo conoscendo i loro nomi.

Se lo avessi capito male, avrei voluto essere corretto. Altrimenti qualcuno dovrebbe probabilmente aggiornare la soluzione sopra citata. Se posso sbagliarmi


7

Questo è quello che uso attualmente, è semplice e funziona perfettamente.

Non devi fare nulla con Google Cloud. Funziona immediatamente con 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: stesso esempio, ma con caricamento:

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

aggiornare:

non è necessario effettuare due chiamate diverse nel metodo di caricamento per ottenere i metadati:

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

1
Come lo useresti con un file che non è codificato in base64?
Tibor Udvari

1
Non è mediaLinkenter, è solo mediaLink
l2aelba

1
Non riesco a trovare mediaLink i.stack.imgur.com/B4Fw5.png
sarah

@ Sarah L'ho scritto usando un dattiloscritto, non sono sicuro che ci sia qualche sostituzione del modulo.
Oliver Dixon

3

Ho avuto lo stesso problema, tuttavia, stavo guardando il codice dell'esempio della funzione Firebase invece del README. E nemmeno le risposte su questo thread hanno aiutato ...

Puoi evitare di passare il file di configurazione effettuando le seguenti operazioni:

Vai alla Cloud Console del tuo progetto > IAM e amministrazione> IAM , trova l'account di servizio predefinito di App Engine e aggiungi il ruolo di creatore del token dell'account di servizio a quel membro. Ciò consentirà alla tua app di creare URL pubblici firmati per le immagini.

fonte: Genera automaticamente le miniature README

Il tuo ruolo per il motore dell'app dovrebbe essere simile a questo:

Cloud Console


3

Se utilizzi il valore predefinito degli elenchi di controllo degli accessi di "publicRead", puoi caricare il file e accedervi con una struttura URL molto semplice:

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

Puoi quindi costruire l'URL in questo modo:

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

2

Funziona se hai solo bisogno di un file pubblico con un semplice URL. Tieni presente che questo potrebbe annullare le regole di archiviazione di 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);
    }
});

1

Per coloro che utilizzano Firebase SDK e admin.initializeApp:

1 - Genera una chiave privata e posizionala nella cartella / functions.

2 - Configura il tuo codice come segue:

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

Documentazione

Il tentativo / cattura è perché sto usando un index.js che importa altri file e crea una funzione per ogni file. Se stai usando un singolo file index.js con tutte le funzioni, dovresti stare bene con admin.initializeApp(Object.assign(functions.config().firebase, { credential: admin.credential.cert(serviceAccount) }));.


per me era "../serviceaccountkey.json" ma grazie per l'avvertenza di usare ../
robert king

1

A partire da Firebase 6.0.0 sono stato in grado di accedere all'archivio direttamente con l'amministratore in questo modo:

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

Quindi non avevo bisogno di aggiungere un account di servizio. Quindi l'impostazione dell'UUID come indicato sopra ha funzionato per ottenere l'URL di Firebase.


1

Questo è il meglio che ho inventato. È ridondante, ma l'unica soluzione ragionevole che ha funzionato per me.

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

1

Senza signedURL()usaremakePublic()

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


0

Se ricevi un errore:

Google Cloud Functions: require (…) non è una funzione

prova questo:

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

0

Ho già pubblicato la mia risposta ... nell'URL sottostante dove puoi ottenere il codice completo con la soluzione

Come faccio a caricare un'immagine con codifica base64 (stringa) direttamente in un bucket di Google Cloud Storage utilizzando 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": "xxxx@xxxx.iam.gserviceaccount.com",
        "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);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.