Pobierz URL pobierania z pliku przesłanego za pomocą Cloud Functions for Firebase

125

Po przesłaniu pliku do Firebase Storage za pomocą Funkcji Firebase chcę uzyskać adres URL pobierania pliku.

Mam to :

...

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

        // Get the download url of file

    });

Plik obiektowy ma wiele parametrów. Nawet jeden nazwany mediaLink. Jeśli jednak spróbuję uzyskać dostęp do tego linku, pojawia się ten błąd:

Użytkownicy anonimowi nie mają dostępu do obiektu storage.objects.get ...

Czy ktoś może mi powiedzieć, jak uzyskać publiczny adres URL pobierania?

Dziękuję Ci

Valentin
źródło
Zobacz także ten post, który rekonstruuje adres URL z danych dostępnych w funkcji.
Kato,

Odpowiedzi:

133

Musisz wygenerować podpisany adres URL przy użyciu getSignedURL za pośrednictwem modułu @ google-cloud / storage NPM.

Przykład:

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

Konieczne będzie zainicjowanie @google-cloud/storageprzy użyciu poświadczeń konta usługi, ponieważ domyślne poświadczenia aplikacji nie będą wystarczające.

AKTUALIZACJA : Dostęp do pakietu Cloud Storage SDK można teraz uzyskać za pośrednictwem pakietu Firebase Admin SDK, który działa jako opakowanie wokół @ google-cloud / storage. Jedynym sposobem jest, jeśli:

  1. Zainicjuj zestaw SDK ze specjalnym kontem usługi, zwykle za pomocą drugiej, innej niż domyślna instancja.
  2. Lub bez konta usługi, nadając domyślnemu kontu usługi App Engine uprawnienie „signBlob”.
James Daniels
źródło
75
To jest dziwne. Adres URL pobierania możemy łatwo uzyskać z referencji Storage podczas korzystania z Firebase Android, iOS i Web SDK. Dlaczego nie jest tak łatwo będąc administratorem? PS: Gdzie mogę znaleźć plik „service-account.json” potrzebny do zainicjowania gcs?
Valentin
2
Dzieje się tak, ponieważ zestaw admin-sdk nie ma żadnych dodatków do Cloud Storage. Możesz pobrać swoje konto usługi admin-sdk json tutaj console.firebase.google.com/project/_/settings/serviceaccounts/ ...
James Daniels
18
URL wygenerowany tą metodą jest absurdalnie długi. Adres URL wygenerowany przez proponowaną metodę @martemorfosis jest znacznie lepszy. Czy jest jakaś funkcja, która generuje ten adres URL? To właśnie zapisuję w bazie danych do wykorzystania w przyszłości, gdy używam firebase-sdk. W domenie funkcji musi istnieć metoda lustrzana.
Bogac
3
Jak możemy przesłać plik service-account.json wraz z wdrożonymi funkcjami? Próbowałem umieścić go w folderze funkcji i odwoływać się do niego w polu pliku w package.json, ale nie jest wdrażany. Dziękuję Ci.
David Aroesti
2
Czy musimy dodawać actioni expires?
Chad Bingham
83

Oto przykład, jak określić token pobierania podczas przesyłania:

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

następnie zadzwoń z

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

Kluczową rzeczą jest to, że metadatawe metadatawłaściwości opcji znajduje się obiekt zagnieżdżony . Ustawienie firebaseStorageDownloadTokenswartości uuid-v4 spowoduje, że Cloud Storage użyje go jako publicznego tokenu uwierzytelniania.

Wielkie dzięki dla @martemorfosis

Drew Beaupre
źródło
Jak uzyskać prawidłowy token UUID dla pliku, który został już przesłany do magazynu? Wygenerowanie losowego UUID nie pomogło. Jakieś wskazówki?
DerFaizio
3
Znalazłem odpowiedź w poście @martemorfosis. Identyfikator UUID można pobrać z pliku object.metadata exports.uploadProfilePic = functions.storage.object (). OnChange (event => {const object = event.data; // The Storage object. Const uuid = object.metadata.firebaseStorageDownloadTokens; // ...
DerFaizio
Dziękuję za przykład wiadra! Próbowałem różnych kombinacji dla metody wiadra i plików przez prawie godzinę :)
JCarlosR
1
Dziękuję za odpowiedź! W moim przypadku przesyłałem za pomocą bucket.file (fileName) .createWriteStream, który nie zwraca danych po zakończeniu przesyłania, w wyniku czego użyłem encodeURIComponent (fileName) zamiast encodeURIComponent (file.name).
Stanislau Buzunko
2
To powinna być właściwa odpowiedź. W rezultacie powstaje adres URL podobny do tego generowanego przez zestawy SDK Firebase (@DevMike) i założę się, że jest to dokładnie to, co robią za kulisami.
Samuel E.
66

Ta odpowiedź zawiera podsumowanie opcji pobierania adresu URL pobierania podczas przesyłania pliku do Google / Firebase Cloud Storage. Istnieją trzy typy adresów URL pobierania:

  1. podpisane adresy URL pobierania, które są tymczasowe i mają funkcje zabezpieczeń
  2. adresy URL pobierania tokenu, które są trwałe i mają funkcje bezpieczeństwa
  3. publiczne adresy URL pobierania, które są trwałe i pozbawione zabezpieczeń

Istnieją trzy sposoby uzyskania adresu URL pobierania tokena. Pozostałe dwa adresy URL pobierania mają tylko jeden sposób, aby je uzyskać.

Z konsoli Firebase Storage

Adres URL pobierania możesz uzyskać z konsoli Firebase Storage:

wprowadź opis obrazu tutaj

Adres URL pobierania wygląda następująco:

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

Pierwsza część to standardowa ścieżka do Twojego pliku. Na końcu jest token. Ten adres URL pobierania jest trwały, tj. Nie wygaśnie, chociaż można go cofnąć.

getDownloadURL () z interfejsu użytkownika

Dokumentacja mówi nam do wykorzystania getDownloadURL():

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

Otrzymuje ten sam adres URL pobierania, który możesz pobrać z konsoli Firebase Storage. Ta metoda jest łatwa, ale wymaga znajomości ścieżki do pliku, który w mojej aplikacji zawiera około 300 linii kodu, aby uzyskać stosunkowo prostą strukturę bazy danych. Jeśli twoja baza danych jest złożona, byłby to koszmar. Możesz przesyłać pliki z poziomu interfejsu użytkownika, ale spowoduje to ujawnienie Twoich danych uwierzytelniających każdemu, kto pobierze Twoją aplikację. Dlatego w przypadku większości projektów będziesz chciał przesłać pliki z zaplecza Node lub Google Cloud Functions, a następnie uzyskać adres URL pobierania i zapisać go w bazie danych wraz z innymi danymi o pliku.

getSignedUrl () dla tymczasowych adresów URL pobierania

getSignedUrl () jest łatwy w użyciu z zaplecza Node lub 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);
            }
          });
        });
      });
    });
  }

Podpisany adres URL pobierania wygląda następująco:

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

Podpisany adres URL ma datę ważności i długi podpis. Dokumentacja dotycząca wiersza poleceń gsutil signurl -d mówi, że podpisane adresy URL są tymczasowe: domyślny okres ważności to jedna godzina, a maksymalny okres ważności to siedem dni.

Mam zamiar ogłosić, że getSignedUrl nigdy nie mówi, że Twój podpisany adres URL wygaśnie za tydzień. Kod dokumentacji ma 3-17-2025datę wygaśnięcia, co sugeruje, że można ustawić lata wygaśnięcia w przyszłości. Moja aplikacja działała idealnie, a tydzień później uległa awarii. Komunikat o błędzie mówił, że podpisy nie pasują, a nie że adres URL pobierania wygasł. Wprowadziłem różne zmiany w moim kodzie i wszystko działało ... aż wszystko się zawiesiło tydzień później. Trwało to ponad miesiąc frustracji.

Udostępnij plik publicznie

Możesz ustawić uprawnienia do publicznego odczytu pliku, jak wyjaśniono w dokumentacji . Można to zrobić z przeglądarki Cloud Storage lub z serwera Node. Możesz ustawić jeden plik jako publiczny, katalog lub całą bazę danych magazynu. Oto kod węzła:

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

Wynik będzie wyglądał następująco w przeglądarce Cloud Storage:

wprowadź opis obrazu tutaj

Każdy może następnie użyć standardowej ścieżki do pobrania pliku:

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

Innym sposobem na upublicznienie pliku jest użycie metody makePublic () . Nie udało mi się to uruchomić, trudno jest poprawnie ustawić zasobnik i ścieżki plików.

Ciekawą alternatywą jest użycie list kontroli dostępu . Możesz udostępnić plik tylko użytkownikom, których umieścisz na liście, lub użyć go, authenticatedReadaby udostępnić plik każdemu, kto jest zalogowany z konta Google. Gdyby istniała opcja „każdy, kto logował się do mojej aplikacji przy użyciu uwierzytelniania Firebase”, użyłbym tej opcji, ponieważ ograniczyłaby dostęp tylko do moich użytkowników.

Zbuduj własny adres URL pobierania za pomocą firebaseStorageDownloadTokens

W kilku odpowiedziach opisano nieudokumentowaną właściwość obiektu Google Storage firebaseStorageDownloadTokens. Dzięki temu możesz wskazać Storage token, którego chcesz użyć. Możesz wygenerować token za pomocą uuidmodułu Node. Cztery wiersze kodu i możesz utworzyć własny adres URL pobierania, ten sam adres URL pobierania, który otrzymasz z konsoli lub getDownloadURL(). Cztery wiersze kodu to:

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

Oto kod w kontekście:

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

To nie jest literówka - musisz zagnieździć się firebaseStorageDownloadTokensw podwójnych warstwach metadata:!

Doug Stevenson zwrócił uwagę, że firebaseStorageDownloadTokensnie jest to oficjalna funkcja Google Cloud Storage. Nie znajdziesz go w żadnej dokumentacji Google i nie ma obietnic, że będzie w przyszłej wersji@google-cloud . Lubię, firebaseStorageDownloadTokensponieważ jest to jedyny sposób na zdobycie tego, czego chcę, ale ma „zapach”, którego nie jest bezpieczny w użyciu.

Dlaczego nie ma metody getDownloadURL () z węzła?

Jak napisał @Clinton, Google powinien utworzyć file.getDownloadURL()metodę w @google-cloud/storage(tj. Zapleczu Twojego Node). Chcę przesłać plik z Google Cloud Functions i uzyskać adres URL pobierania tokena.

Thomas David Kehoe
źródło
11
Utworzyłem numer w @google-cloud/storagetym celu, możesz dać mu +1;) github.com/googleapis/nodejs-storage/issues/697
Théo Champion
1
najnowszy link makePublic () .
galki
1
Wygląda na firebaseStorageDownloadTokensto, że już nie działa.
Mason
1
Zaakceptowana odpowiedź sugeruje, że nie jest możliwe uzyskanie trwałego adresu URL pobierania, który nie wygasa, co jest nieprawidłowe. Szczegółowość Twojej odpowiedzi jest doskonała i powinna zostać oznaczona jako poprawna. Dziękuję Ci.
DevMike
2
@thomas dzięki za niesamowite podsumowanie! Wspomniałeś, że istnieją 3 sposoby uzyskania trwałego adresu URL pobierania tokenu, ale udostępniłeś tylko 2: (a) z konsoli Firebase Storage i (b) getDownloadURL () z interfejsu użytkownika. Zastanawiam się, jaki jest trzeci sposób?
czphilip
23

Dzięki niedawnym zmianom w odpowiedzi obiektu funkcji możesz uzyskać wszystko, czego potrzebujesz, aby „zszyć” adres URL pobierania w następujący sposób:

 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
źródło
2
Czy odnosisz się do odpowiedzi obiektu z bucket.file().upload()? Nie otrzymuję żadnej właściwości metadanych w danych odpowiedzi i nie jestem pewien, jak je uzyskać firebaseStorageDownloadTokens.
Dygerati,
także [YOUR BUCKET] to bucket.name, że nie musisz go na stałe
kodować
4
Problem z tym rozwiązaniem polega na tym, że adres URL usługi jest zakodowany na stałe. Jeśli Firebase / Google to zmieni, może się zepsuć. Korzystanie z metadata.mediaLinknieruchomości zapobiega takim problemom.
Laurent
2
Tworzenie takiego adresu URL nie jest obsługiwane. Może działać dzisiaj, ale może się zepsuć w przyszłości. Do wygenerowania prawidłowego adresu URL pobierania należy używać wyłącznie udostępnionych interfejsów API.
Doug Stevenson
1
Poleganie na zakodowanym na stałe adresie URL, który może ulec zmianie, to zły wybór.
Laurent
23

Jeśli pracujesz nad projektem Firebase, możesz tworzyć podpisane adresy URL w Cloud Function bez dołączania innych bibliotek lub pobierania pliku danych logowania. Wystarczy włączyć IAM API i dodać rolę do istniejącego konta usługi (patrz poniżej).

Zainicjuj bibliotekę administratora i uzyskaj odniesienie do pliku w normalny sposób:

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

Następnie generujesz podpisany adres URL z

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

Upewnij się, że Twoje konto usługi Firebase ma wystarczające uprawnienia, aby to uruchomić

  1. Przejdź do konsoli Google API i włącz IAM API ( https://console.developers.google.com/apis/api/iam.googleapis.com/overview )
  2. Nadal w konsoli API przejdź do menu głównego „IAM & admin” -> „IAM”
  3. Kliknij edytuj przy roli „Domyślne konto usługi App Engine”
  4. Kliknij „Dodaj kolejną rolę” i dodaj rolę o nazwie „Twórca tokena konta usługi”
  5. Zapisz i poczekaj minutę, aż zmiany zostaną rozpowszechnione

W przypadku podstawowej konfiguracji Firebase przy pierwszym uruchomieniu powyższego kodu pojawi się błąd API zarządzania tożsamością i dostępem (IAM), które nie było wcześniej używane w projekcie XXXXXX lub jest wyłączone. . Jeśli klikniesz łącze w komunikacie o błędzie i włączysz IAM API, pojawi się kolejny błąd: Uprawnienie iam.serviceAccounts.signBlob jest wymagane do wykonania tej operacji na koncie usługi moje-konto-usługi . Dodanie roli Twórca tokenów rozwiązuje ten drugi problem z uprawnieniami.

SMX
źródło
Właśnie miałem zostawić odpowiedź z zasadniczo tymi samymi szczegółami, które W KOŃCU znalazłem na własnej skórze - na pewno żałuję, że nie czytałem rozwiązań tak daleko wcześniej: / To działało dla mnie od 12.12.18. Dzięki za szczegółowe instrukcje, bardzo pomocne dla nas początkujących !!
Kat
2
Mój signedurl wygasa za 2 tygodnie, ale używam admin.initializeApp () bez klucza, czy to jest problem? Miałem domyślne konto usługi App Engine ustawione na „właściciel” i agenta usługi Cloud Functions. Właśnie usunąłem „właściciela” na razie i dodałem „Twórca tokena konta usługi”
Amit Bravo,
2
Podpisane adresy URL wygasają za 7 dni. Możesz ustawić krótszą datę ważności, ale nie dłużej.
Thomas David Kehoe
Jak odświeżyć adres URL, jeśli wygaśnie?
Manoj MM,
jak odświeżyć adres URL, aby ustawić go na dłuższy czas?
Saifallak
17

Jedną z metod, których używam z powodzeniem, jest ustawienie wartości UUID v4 na klucz nazwany firebaseStorageDownloadTokensw metadanych pliku po zakończeniu przesyłania, a następnie samodzielne zestawienie adresu URL pobierania zgodnie ze strukturą używaną przez Firebase do generowania tych adresów URL, np .:

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

Nie wiem, jak bardzo „bezpieczne” jest używanie tej metody (biorąc pod uwagę, że Firebase może zmienić sposób generowania adresów URL pobierania w przyszłości), ale jest łatwa do wdrożenia.

martemorfoza
źródło
1
Czy masz przykład, w którym ustawiasz wartość uuid?
Drew Beaupre
1
Mam to samo pytanie co Drew, gdzie ustawiasz metadane? Próbowałem ustawić, gdy funkcja bucket.upload nie działa.
Vysakh Sreenivasan
1
Vysakh, zamieściłem pełną odpowiedź z przykładem. Mam nadzieję, że ci to pomoże.
Drew Beaupre
Gdzie / jak tworzysz token?
CodyBugstein,
3
Nie uważałbym tej techniki za „bezpieczną”, ponieważ adresy URL pobierania mają być nieprzejrzyste, a ich komponenty nie powinny być rozkładane ani montowane.
Doug Stevenson
16

Dla tych, którzy zastanawiają się, gdzie powinien znaleźć się plik serviceAccountKey.json pakietu Firebase Admin SDK. Po prostu umieść go w folderze funkcji i wdróż jak zwykle.

Wciąż zdumiewa mnie, dlaczego nie możemy po prostu pobrać adresu URL z metadanych, tak jak robimy to w SDK JavaScript. Generowanie adresu URL, który ostatecznie wygaśnie, i zapisanie go w bazie danych nie jest pożądane.

Clinton
źródło
16

Należy unikać przedrostka adresu URL harcoding w swoim kodzie . Proponuję skorzystać z opcji predefinedAcl: 'publicRead'przy wgrywaniu pliku z Cloud Storage NodeJS 1.6.x lub +:

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

bucket.upload(attachment, options);

Uzyskanie publicznego adresu URL jest wtedy tak proste, jak:

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
źródło
2
To faktycznie wydaje się działać. Jedynym minusem, jaki do tej pory widzę, jest to, że jeśli trafisz na obraz w pasku adresu przeglądarki, pobierze obraz zamiast wyświetlać go w tekście.
Michael Giovanni Pumo
file.getMetadata () załatwiło mi sprawę po użyciu metody save () w odniesieniu do pliku. Używanie go w NodeJS z firebase-admin sdk.
Pascal Lamers
nie zadziałało, otrzymuję Anonimowy rozmówca nie ma dostępu do storage.objects.get do your_app / image.jpg
Manoj MM
9

Przepraszamy, ale nie mogę opublikować komentarza do Twojego pytania powyżej z powodu braku reputacji, więc dołączę go do tej odpowiedzi.

Zrób tak, jak opisano powyżej, generując podpisany adres URL, ale zamiast korzystać z service-account.json, myślę, że musisz użyć serviceAccountKey.json, który możesz wygenerować pod adresem (odpowiednio zastąp YOURPROJECTID)

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

Przykład:

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
źródło
9

Nie mogę komentować odpowiedzi, której udzielił James Daniels, ale myślę, że jest to bardzo ważne do przeczytania.

Wydanie podpisanego adresu URL Wydaje się, że w wielu przypadkach jest dość zły i prawdopodobnie Niebezpieczny . Zgodnie z dokumentacją Firebase, podpisany adres URL wygasa po pewnym czasie, więc dodanie go do bazy danych doprowadzi do pustego adresu URL po pewnym czasie

Może się zdarzyć, że dokumentacja została tam źle zrozumiana, a podpisany adres URL nie wygasa, co spowodowałoby pewne problemy z bezpieczeństwem. Klucz wydaje się być taki sam dla każdego przesłanego pliku. Oznacza to, że po uzyskaniu adresu URL jednego pliku ktoś może łatwo uzyskać dostęp do plików, do których nie powinien mieć dostępu, znając tylko ich nazwy.

Gdybym tego nie zrozumiał, wolałbym zostać poprawiony. W przeciwnym razie ktoś powinien prawdopodobnie zaktualizować powyższe rozwiązanie. Jeśli się mylę

Renji
źródło
8

To jest to, czego obecnie używam, jest proste i działa bez zarzutu.

Nie musisz nic robić z Google Cloud. Działa po wyjęciu z pudełka z 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

EDYCJA: ten sam przykład, ale z przesyłaniem:

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

#update: nie ma potrzeby wykonywania dwóch różnych wywołań w metodzie przesyłania, aby uzyskać metadane:

let file = await bucket.upload(fromFilePath, {destination: toFilePath});
const trimUrl = file[0].metaData.mediaLink
Oliver Dixon
źródło
1
Jak użyłbyś tego z plikiem, który nie jest zakodowany w formacie base64?
Tibor Udvari
1
To nie mediaLinkenter, tylko mediaLink
l2aelba
1
Nie mogę znaleźć mediaLink i.stack.imgur.com/B4Fw5.png
sarah,
@Sarah Napisałem to na maszynie, nie jestem pewien, czy jest jakaś wymiana modułu.
Oliver Dixon
4

Jeśli używasz wstępnie zdefiniowanej wartości list kontroli dostępu „publicRead”, możesz przesłać plik i uzyskać do niego dostęp z bardzo prostą strukturą adresu URL:

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

Następnie możesz utworzyć adres URL w następujący sposób:

const storageRoot = 'https://storage.googleapis.com/';
const bucketName = 'myapp.appspot.com/'; // CHANGE TO YOUR BUCKET NAME
const downloadUrl = storageRoot + bucketName + encodeURIComponent(dest);
nieorganik
źródło
3

Miałem ten sam problem, jednak patrzyłem na kod przykładu funkcji firebase zamiast README. Odpowiedzi w tym wątku też nie pomogły ...

Możesz uniknąć przekazywania pliku konfiguracyjnego, wykonując następujące czynności:

Przejdź do Cloud Console swojego projektu > IAM & admin> IAM , znajdź domyślne konto usługi App Engine i dodaj rolę twórcy tokenu konta usługi do tego członka. Umożliwi to Twojej aplikacji tworzenie podpisanych publicznych adresów URL do obrazów.

źródło: funkcja automatycznego generowania miniatur README

Twoja rola dla silnika aplikacji powinna wyglądać następująco:

Cloud Console

TheFullResolution
źródło
2

Działa to, jeśli potrzebujesz tylko publicznego pliku z prostym adresem URL. Pamiętaj, że może to unieważnić reguły przechowywania w 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
źródło
1

Dla tych, którzy używają Firebase SDK i admin.initializeApp:

1 - Wygeneruj klucz prywatny i umieść go w folderze / functions.

2 - Skonfiguruj swój kod w następujący sposób:

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

Dokumentacja

Try / catch jest spowodowane tym, że używam pliku index.js, który importuje inne pliki i tworzy jedną funkcję dla każdego pliku. Jeśli używasz jednego pliku index.js ze wszystkimi funkcjami, powinieneś być w porządku admin.initializeApp(Object.assign(functions.config().firebase, { credential: admin.credential.cert(serviceAccount) }));.

Allan Poppe
źródło
dla mnie to było „../serviceaccountkey.json”, ale dzięki za głowice do korzystania z ../
robert króla
1

Od Firebase 6.0.0 mogłem uzyskać dostęp do pamięci bezpośrednio z administratorem w następujący sposób:

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

Więc nie musiałem dodawać konta usługi. Następnie ustawienie UUID, jak wspomniano powyżej, pomogło w uzyskaniu adresu URL Firebase.

NickJ
źródło
1

To najlepsze, co wymyśliłem. Jest to zbędne, ale jedyne rozsądne rozwiązanie, które u mnie zadziałało.

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
źródło
1

Bez signedURL()użyciamakePublic()

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
źródło
1

odpowiedź przez https://stackoverflow.com/users/269447/laurent działa najlepiej

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
źródło
0

Jeśli otrzymujesz błąd:

Funkcje Google Cloud: require (…) nie jest funkcją

Spróbuj tego:

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);
.....
Dzika koza
źródło
0

Już wysyłam moje odpowiedzi ... w poniższy adres URL, gdzie można uzyskać pełny kod z rozwiązaniem

Jak przesłać obraz (ciąg znaków) zakodowany w base64 bezpośrednio do zasobnika Google Cloud Storage przy użyciu 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
źródło