Obtenha o URL de download do arquivo enviado com Cloud Functions para Firebase

124

Depois de fazer upload de um arquivo no Firebase storage com funções para Firebase, gostaria de obter o URL de download do arquivo.

Eu tenho isto :

...

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

        // Get the download url of file

    });

O arquivo objeto tem muitos parâmetros. Mesmo um nomeado mediaLink. No entanto, se tento acessar este link, recebo este erro:

Usuários anônimos não têm acesso storage.objects.get ao objeto ...

Alguém pode me dizer como obter o URL público de download?

Obrigado

Valentin
fonte
Veja também este post que reconstrói a URL a partir dos dados disponíveis na função.
Kato de

Respostas:

133

Você precisará gerar um URL assinado usando getSignedURL por meio do módulo NPM @ google-cloud / storage .

Exemplo:

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

Você precisará inicializar @google-cloud/storagecom suas credenciais de conta de serviço, pois as credenciais padrão do aplicativo não serão suficientes.

ATUALIZAÇÃO : o Cloud Storage SDK agora pode ser acessado por meio do Firebase Admin SDK, que atua como um wrapper em torno de @ google-cloud / storage. A única maneira é se você:

  1. Inicie o SDK com uma conta de serviço especial, normalmente por meio de uma segunda instância não padrão.
  2. Ou, sem uma conta de serviço, dando à conta de serviço padrão do App Engine a permissão "signBlob".
James Daniels
fonte
74
Isto é estranho. Podemos obter facilmente o URL de download de uma referência de armazenamento ao usar o Firebase Android, iOS e Web SDK. Por que não é tão fácil ser administrador? PS: Onde posso encontrar o 'service-account.json' necessário para inicializar o gcs?
Valentin
2
Isso ocorre porque o admin-sdk não tem nenhuma adição do Cloud Storage. Você pode obter sua conta de serviço sdk de administrador json aqui console.firebase.google.com/project/_/settings/serviceaccounts/…
James Daniels
18
O URL gerado com este método é ridiculamente longo. A URL gerada pelo método proposto por @martemorfosis é muito melhor. Existe alguma função que produz esse URL? Isso é o que eu salvo no banco de dados para uso futuro quando estou usando firebase-sdk. Um método de espelho precisa existir no domínio Funções.
Bogac,
3
Como podemos carregar o service-account.json junto com as funções implantadas? Tentei colocá-lo na pasta de funções e fazer referência a ele no campo de arquivo em package.json, mas ele não está sendo implantado. Obrigado.
David Aroesti,
2
Somos obrigados a adicionar actione expires?
Chad Bingham de
82

Aqui está um exemplo de como especificar o token de download no upload:

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

então ligue com

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

O principal aqui é que há um metadataobjeto aninhado na metadatapropriedade option. Definir firebaseStorageDownloadTokenscomo um valor uuid-v4 dirá ao Cloud Storage para usá-lo como seu token de autenticação público.

Muito obrigado a @martemorfosis

Drew Beaupre
fonte
Como obtenho um token UUID válido para um arquivo que já foi carregado no armazenamento? Gerar UUID aleatório não ajudou. Quaisquer dicas?
DerFaizio
3
Encontrei a resposta no post @martemorfosis. O UUID pode ser recuperado de object.metadata exports.uploadProfilePic = functions.storage.object (). OnChange (event => {const object = event.data; // O objeto Storage. Const uuid = object.metadata.firebaseStorageDownloadTokens; // ...
DerFaizio
Obrigado pelo exemplo do balde! Eu estava tentando combinações diferentes para os métodos de intervalo e arquivo por quase 1 hora :)
JCarlosR
1
Obrigado pela sua resposta! No meu caso, eu estava carregando com bucket.file (fileName) .createWriteStream que não retorna dados quando termina o upload, como resultado, usei encodeURIComponent (fileName) em vez de encodeURIComponent (file.name).
Stanislau Buzunko
2
Esta deve ser a resposta certa. Isso resulta em um URL semelhante ao gerado pelos SDKs do Firebase (@DevMike), e eu aposto que é exatamente o que eles fazem nos bastidores.
Samuel E.
64

Esta resposta resumirá as opções para obter o URL de download ao enviar um arquivo para o Google / Firebase Cloud Storage. Existem três tipos de URLS de download:

  1. URLs de download assinados, que são temporários e possuem recursos de segurança
  2. URLs de download de tokens, que são persistentes e possuem recursos de segurança
  3. URLs de download público, que são persistentes e carecem de segurança

Existem três maneiras de obter um URL de download de token. Os outros dois URLs de download têm apenas uma maneira de obtê-los.

No Firebase Storage Console

Você pode obter o URL de download no console do Firebase Storage:

insira a descrição da imagem aqui

O URL de download é parecido com este:

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

A primeira parte é um caminho padrão para o seu arquivo. No final está o token. Este URL de download é permanente, ou seja, não irá expirar, embora você possa revogá-lo.

getDownloadURL () no front end

A documentação nos diz para usar getDownloadURL():

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

Obtém o mesmo URL de download que você pode obter no console do Firebase Storage. Esse método é fácil, mas requer que você saiba o caminho para o seu arquivo, que em meu aplicativo tem cerca de 300 linhas de código, para uma estrutura de banco de dados relativamente simples. Se o seu banco de dados for complexo, isso seria um pesadelo. E você pode fazer upload de arquivos do front end, mas isso exporia suas credenciais para qualquer pessoa que baixasse seu aplicativo. Portanto, para a maioria dos projetos, você desejará fazer upload de seus arquivos do back-end do Node ou do Google Cloud Functions e, em seguida, obter o URL de download e salvá-lo em seu banco de dados junto com outros dados sobre seu arquivo.

getSignedUrl () para URLs de download temporário

getSignedUrl () é fácil de usar em um back end do Node ou no 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);
            }
          });
        });
      });
    });
  }

Um URL de download assinado tem a seguinte aparência:

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

O URL assinado tem uma data de validade e assinatura longa. A documentação da linha de comando gsutil signurl -d diz que os URLs assinados são temporários: a expiração padrão é de uma hora e a expiração máxima é de sete dias.

Vou reclamar aqui que getSignedUrl nunca diz que seu URL assinado irá expirar em uma semana. O código da documentação tem 3-17-2025como data de validade, sugerindo que você pode definir os anos de expiração no futuro. Meu aplicativo funcionou perfeitamente e travou uma semana depois. A mensagem de erro dizia que as assinaturas não correspondiam, não que o URL de download havia expirado. Fiz várias alterações no meu código e tudo funcionou ... até que tudo travou uma semana depois. Isso durou mais de um mês de frustração.

Disponibilize seu arquivo publicamente

Você pode definir as permissões em seu arquivo para leitura pública, conforme explicado na documentação . Isso pode ser feito no navegador do Cloud Storage ou no servidor Node. Você pode tornar um arquivo público ou um diretório ou todo o banco de dados do Storage. Aqui está o código do Node:

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

O resultado será parecido com o seguinte no navegador do Cloud Storage:

insira a descrição da imagem aqui

Qualquer pessoa pode usar o caminho padrão para fazer o download do seu arquivo:

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

Outra maneira de tornar um arquivo público é usar o método makePublic () . Eu não consegui fazer isso funcionar, é complicado acertar os caminhos do intervalo e do arquivo.

Uma alternativa interessante é usar listas de controle de acesso . Você pode disponibilizar um arquivo apenas para usuários que você colocou em uma lista ou usar authenticatedReadpara disponibilizar o arquivo para qualquer pessoa que esteja conectada a uma conta do Google. Se houvesse uma opção "qualquer pessoa que se conectou ao meu aplicativo usando Firebase Auth", eu a usaria, pois limitaria o acesso apenas aos meus usuários.

Crie seu próprio URL de download com firebaseStorageDownloadTokens

Várias respostas descrevem uma propriedade de objeto do Google Storage não documentada firebaseStorageDownloadTokens. Com isso, você pode informar ao Storage o token que deseja usar. Você pode gerar um token com o uuidmódulo Node. Quatro linhas de código e você pode construir seu próprio URL de download, o mesmo URL de download que obtém no console ou getDownloadURL(). As quatro linhas de código são:

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

Aqui está o código em contexto:

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

Isso não é um erro de digitação - você deve aninhar firebaseStorageDownloadTokensem camadas duplas de metadata:!

Doug Stevenson apontou que firebaseStorageDownloadTokensnão é um recurso oficial do Google Cloud Storage. Você não o encontrará em nenhuma documentação do Google e não há promessa de que será em uma versão futura do @google-cloud. Gosto firebaseStorageDownloadTokensporque é a única maneira de conseguir o que quero, mas tem um "cheiro" que não é seguro usar.

Por que não getDownloadURL () do Node?

Como escreveu @Clinton, o Google deve criar um file.getDownloadURL()método em @google-cloud/storage(ou seja, seu back end do Node). Quero fazer upload de um arquivo do Google Cloud Functions e obter o URL de download do token.

Thomas David Kehoe
fonte
10
Criei um problema @google-cloud/storagepara isso, fique
Théo Champion
1
último link makePublic () .
galki
1
Parece que o firebaseStorageDownloadTokensnão funciona mais.
Mason
1
A resposta aceita sugere que não é possível obter um URL de download persistente que não expira, o que não é correto. O detalhe aqui em sua resposta é excelente e deve ser marcado como a resposta correta. Obrigado.
DevMike de
2
@thomas, obrigado pelo resumo incrível! Você mencionou que há três maneiras de obter um URL de download de token persistente, mas compartilhou apenas 2: (a) no Console do Firebase Storage e (b) getDownloadURL () no front end. Eu me pergunto qual é a 3ª via?
czphilip
23

Com as mudanças recentes na resposta do objeto de funções, você pode obter tudo o que precisa para "costurar" o URL de download, como:

 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
fonte
2
Você está se referindo à resposta do objeto de bucket.file().upload()? Não recebo nenhuma propriedade de metadados nos dados de resposta e não tenho certeza de como obtê-los firebaseStorageDownloadTokens.
Dygerati de
também [YOUR BUCKET] é bucket.name, você não precisa codificá-lo ou usar uma var local extra
Călin Darie
4
O problema com esta solução é que o URL do serviço está codificado. Se o Firebase / Google mudar, ele pode quebrar. Usar a metadata.mediaLinkpropriedade evita esse problema.
Laurent
2
Não é um caso suportado para construir um URL como este. Pode funcionar hoje, mas pode quebrar no futuro. Você só deve usar as APIs fornecidas para gerar um URL de download adequado.
Doug Stevenson
1
Depender de um URL codificado que pode mudar é uma escolha ruim.
Laurent
23

Se você estiver trabalhando em um projeto do Firebase, poderá criar URLs assinados em um Cloud Function sem incluir outras bibliotecas ou baixar um arquivo de credenciais. Você só precisa habilitar a API IAM e adicionar uma função à sua conta de serviço existente (veja abaixo).

Inicialize a biblioteca de administração e obtenha uma referência de arquivo como faria 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')

Você então gera um URL assinado com

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

Certifique-se de que sua conta de serviço do Firebase tenha permissões suficientes para executar este

  1. Vá para o console de API do Google e ative a API IAM ( https://console.developers.google.com/apis/api/iam.googleapis.com/overview )
  2. Ainda no console da API, vá para o menu principal, "IAM e admin" -> "IAM"
  3. Clique em editar para a função "Conta de serviço padrão do App Engine"
  4. Clique em "Adicionar outra função" e adicione a chamada "Criador de token de conta de serviço"
  5. Salve e aguarde um minuto para que as alterações sejam propagadas

Com uma configuração do Firebase vanilla, na primeira vez que executar o código acima, você receberá um erro A API Identity and Access Management (IAM) não foi usada no projeto XXXXXX antes ou está desativada. . Se você seguir o link na mensagem de erro e ativar a API IAM, receberá outro erro: Permission iam.serviceAccounts.signBlob é necessário para executar esta operação na conta de serviço my-service-account . Adicionar a função Token Creator corrige esse segundo problema de permissão.

SMX
fonte
Eu estava prestes a deixar uma resposta com basicamente os mesmos detalhes que FINALMENTE descobri da maneira mais difícil - com certeza gostaria de ter lido as soluções tão cedo: / Isso funcionou para mim em 12/12/18. Obrigado pelas instruções detalhadas, muito úteis para nós iniciantes !!
Kat
2
Meu assinadourl expira em 2 semanas, mas estou usando admin.initializeApp () sem chave. Esse é o problema? Eu tinha a conta de serviço padrão do aplicativo App Engine definida como "proprietário" e Agente de serviço do Cloud Functions, acabei de remover "proprietário" por enquanto e adicionei "Criador de token de conta de serviço"
Amit Bravo
2
Os URLs assinados expiram em 7 dias. Você pode definir uma data de expiração mais curta, mas não mais longa.
Thomas David Kehoe
Como atualizar o url se ele expirar?
Manoj MM
como atualizar o url para configurá-lo para mais tempo?
Saifallak de
17

Um método que estou usando com sucesso é definir um valor UUID v4 para uma chave nomeada firebaseStorageDownloadTokensnos metadados do arquivo após terminar o upload e, em seguida, montar o URL de download eu mesmo seguindo a estrutura que o Firebase usa para gerar esses URLs, por exemplo:

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

Não sei o quanto "seguro" é usar esse método (visto que o Firebase pode mudar a forma como gera os URLs de download no futuro), mas é fácil de implementar.

martemorfose
fonte
1
Você tem um exemplo em que define o valor uuid?
Drew Beaupre
1
Tenho a mesma pergunta que Drew, onde você define os metadados? Tentei definir a função while bucket.upload, não funcionou.
Vysakh Sreenivasan
1
Vysakh, postei uma resposta completa com exemplo. Espero que ajude você.
Drew Beaupre
Onde / como você cria o token?
CodyBugstein
3
Eu não consideraria essa técnica "segura", pois os URLs de download são opacos, cujos componentes não devem ser decompostos ou montados.
Doug Stevenson
16

Para aqueles que estão se perguntando onde o arquivo serviceAccountKey.json do SDK Admin do Firebase deve ir. Basta colocá-lo na pasta de funções e implantar normalmente.

Ainda me confunde por que não podemos simplesmente obter o URL de download dos metadados como fazemos no SDK Javascript. Não é desejável gerar uma url que irá expirar e salvá-la no banco de dados.

Clinton
fonte
15

Eu sugiro usar a opção predefinedAcl: 'publicRead'ao enviar um arquivo com Cloud Storage NodeJS 1.6.x ou +:

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

bucket.upload(attachment, options);

Então, obter o URL público é tão simples como:

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
fonte
2
Isso de fato parece funcionar. A única desvantagem que vejo até agora é que se você clicar na imagem na barra de URL de um navegador, ele fará o download da imagem em vez de visualizá-la embutida.
Michael Giovanni Pumo
file.getMetadata () fez o truque para mim depois de usar o método save () na referência do arquivo. Usando-o em NodeJS com firebase-admin sdk.
Pascal Lamers
não funcionou, estou recebendo uma chamada anônima que não tem storage.objects.get access to your_app / image.jpg
Manoj MM
9

Desculpe, mas não posso postar um comentário para sua pergunta acima por causa da falta de reputação, então vou incluí-lo nesta resposta.

Faça conforme indicado acima, gerando um Url assinado, mas em vez de usar o service-account.json, acho que você deve usar o serviceAccountKey.json que pode ser gerado em (substitua YOURPROJECTID de acordo)

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

Exemplo:

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
fonte
9

Não posso comentar a resposta que James Daniels deu, mas acho muito importante ler isso.

Distribuir uma URL assinada como ele fez parece, em muitos casos, muito ruim e possivelmente perigoso . De acordo com a documentação do Firebase, o url assinado expira depois de algum tempo, portanto, adicioná-lo ao seu banco de dados levará a um url vazio após um determinado período

Pode ser que a documentação não tenha entendido bem e a url assinada não expire, o que causaria alguns problemas de segurança. A chave parece ser a mesma para todos os arquivos carregados. Isso significa que, uma vez que você obteve o url de um arquivo, alguém poderia acessar facilmente os arquivos que não deveria acessar, apenas por saber seus nomes.

Se eu não entendesse isso, gostaria de ser corrigido. Caso contrário, alguém provavelmente deveria atualizar a solução nomeada acima. Se eu posso estar errado lá

Renji
fonte
7

É o que eu uso atualmente, é simples e funciona perfeitamente.

Você não precisa fazer nada com o Google Cloud. Funciona imediatamente com o 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: Mesmo exemplo, mas com upload:

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

atualizar:

não há necessidade de fazer duas chamadas diferentes no método de upload para obter os metadados:

let file = await bucket.upload(fromFilePath, {destination: toFilePath});
const trimUrl = file[0].metaData.mediaLink
Oliver Dixon
fonte
1
Como você o usaria com um arquivo que não é codificado em base64?
Tibor Udvari de
1
Não é mediaLinkenter, é apenas mediaLink
l2aelba
1
Não consigo encontrar mediaLink i.stack.imgur.com/B4Fw5.png
sarah
@Sarah eu escrevi isso usando o typescript, não tenho certeza se há alguma substituição de módulo.
Oliver Dixon
3

Eu tive o mesmo problema, no entanto, estava olhando para o código do exemplo da função firebase em vez do README. E as respostas neste tópico também não ajudaram ...

Você pode evitar passar o arquivo de configuração fazendo o seguinte:

Acesse o Cloud Console do seu projeto > IAM e administrador> IAM , encontre a conta de serviço padrão do App Engine e adicione o papel Criador de token de conta de serviço a esse membro. Isso permitirá que seu aplicativo crie URLs públicos assinados para as imagens.

fonte: Função de geração automática de miniaturas README

Sua função para o App Engine deve ser assim:

Cloud Console

TheFullResolution
fonte
3

Se você usar o valor predefinido de listas de controle de acesso de 'publicRead', poderá fazer upload do arquivo e acessá-lo com uma estrutura de url muito simples:

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

Você pode então construir o url assim:

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

Isso funciona se você precisar apenas de um arquivo público com uma URL simples. Observe que isso pode anular suas regras de armazenamento do 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
fonte
1

Para aqueles que usam Firebase SDK e admin.initializeApp:

1 - Gere uma Chave Privada e coloque na pasta / funções.

2 - Configure seu código da seguinte maneira:

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

Documentação

O try / catch é porque estou usando um index.js que importa outros arquivos e cria uma função para cada arquivo. Se você estiver usando um único arquivo index.js com todas as funções, não terá problemas admin.initializeApp(Object.assign(functions.config().firebase, { credential: admin.credential.cert(serviceAccount) }));.

Allan Poppe
fonte
para mim era '../serviceaccountkey.json', mas obrigado pelo aviso para usar o ../
robert king
1

A partir do Firebase 6.0.0, consegui acessar o armazenamento diretamente com o administrador desta forma:

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

Portanto, não precisei adicionar uma conta de serviço. Então, definir o UUID conforme mencionado acima funcionou para obter o url do firebase.

NickJ
fonte
1

Este é o melhor que descobri. É redundante, mas a única solução razoável que funcionou para mim.

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
fonte
1

Sem signedURL()usarmakePublic()

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
fonte
1

responder por https://stackoverflow.com/users/269447/laurent funciona melhor

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
fonte
0

Se você estiver recebendo um erro:

Funções do Google Cloud: require (…) não é uma função

tente isto:

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);
.....
Cabra Selvagem
fonte
0

Eu já postei minha ans ... na URL abaixo Onde você pode obter o código completo com a solução

Como faço o upload de uma imagem codificada em base64 (string) diretamente para um intervalo do Google Cloud Storage usando 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
fonte