Получить URL-адрес загрузки из файла, загруженного с помощью облачных функций для Firebase
После загрузки файла в Firebase Storage с функциями для Firebase я хотел бы получить URL-адрес загрузки файла.
У меня есть это:
...
return bucket
.upload(fromFilePath, {destination: toFilePath})
.then((err, file) => {
// Get the download url of file
});
Объектный файл имеет много параметров. Даже один по имени mediaLink
. Однако, если я пытаюсь получить доступ к этой ссылке, я получаю эту ошибку:
Анонимные пользователи не имеют доступа к объекту storage.objects.get...
Может кто-нибудь сказать мне, как получить общедоступный URL-адрес загрузки?
Спасибо
Ответы
Ответ 1
Вам нужно будет создать подписанный URL-адрес, используя getSignedURL через модуль NPM @google-cloud/storage.
Пример:
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 public URL
});
Вам нужно будет инициализировать @google-cloud/storage
с помощью учетных данных учетной записи службы, так как учетных данных приложения по умолчанию будет недостаточно.
ОБНОВЛЕНИЕ: к Cloud Storage SDK теперь можно получить доступ через Firebase Admin SDK, который действует как обертка вокруг @google-cloud/storage. Единственный способ это сделать, если вы либо:
- Запустите SDK со специальной учетной записью службы, обычно через второй экземпляр, отличный от используемого по умолчанию.
- Или без учетной записи службы, предоставив учетной записи службы App Engine по умолчанию разрешение "signBlob".
Ответ 2
Вот пример того, как указать токен загрузки при загрузке:
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);
});
}
затем вызовите с помощью
upload(localPath, remotePath).then( downloadURL => {
console.log(downloadURL);
});
Главное, что существует объект metadata
, вложенный в свойство опции metadata
. Установка firebaseStorageDownloadTokens
на значение uuid-v4 сообщит Cloud Storage, чтобы использовать это как общедоступный токен аутентификации.
Большое спасибо @martemorfosis
Ответ 3
Если вы работаете над проектом Firebase, вы можете создавать подписанные URL-адреса в облачной функции, не включая другие библиотеки и не загружая файл учетных данных. Вам просто нужно включить IAM API и добавить роль в существующую учетную запись службы (см. Ниже).
Инициализируйте библиотеку администратора и получите ссылку на файл, как обычно:
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')
Затем вы создаете подписанный URL с
myFile.getSignedUrl({action: 'read', expires: someDateObj}).then(urls => {
const signedUrl = urls[0]
})
Убедитесь, что ваша учетная запись службы Firebase имеет достаточные разрешения для запуска этого
- Перейдите в консоль API Google и включите API IAM (https://console.developers.google.com/apis/api/iam.googleapis.com/overview).
- В консоли API перейдите в главное меню "IAM & admin" → "IAM".
- Нажмите "Изменить" для роли "Учетная запись службы App Engine по умолчанию".
- Нажмите "Добавить другую роль" и добавьте роль "Создатель токена учетной записи службы".
- Сохраните и подождите минуту, чтобы изменения распространялись
С ванильной конфигурацией Firebase при первом запуске приведенного выше кода вы получите сообщение об ошибке API Identity and Access Management (IAM) ранее не использовался в проекте XXXXXX или он отключен. Если перейти по ссылке в сообщении об ошибке и включив IAM API, вы получите еще одну ошибку: для выполнения этой операции на учетной записи службы my-service-account требуется разрешение iam.serviceAccounts.signBlob. Добавление роли Token Creator устраняет эту вторую проблему с разрешениями.
Ответ 4
Благодаря последним изменениям в ответе объекта функций вы можете получить все, что вам нужно, чтобы "сшить" вместе URL-адрес загрузки следующим образом:
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);
Ответ 5
В этом ответе будут представлены варианты получения URL-адреса для загрузки при загрузке файла в Google/Firebase Cloud Storage. Существует три типа URL для загрузки:
- подписанные URL-адреса для загрузки, которые являются временными и имеют функции безопасности
- URL загрузки токенов, которые являются постоянными и имеют функции безопасности
- общедоступные URL-адреса для загрузки, которые являются постоянными и не имеют безопасности
Есть три способа получить URL для загрузки токена. У двух других URL-адресов для загрузки есть только один способ получить их.
Из консоли хранилища Firebase
Вы можете получить URL для загрузки из консоли Firebase Storage:
![enter image description here]()
URL-адрес загрузки выглядит следующим образом:
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
Первая часть - это стандартный путь к вашему файлу. В конце есть жетон. Этот URL-адрес загрузки является постоянным, то есть срок его действия не истекает, хотя вы можете отозвать его.
getDownloadURL() из внешнего интерфейса
документация говорит нам использовать getDownloadURL()
:
let url = await firebase.storage().ref('Audio/English/United_States-OED-' + i +'/' + $scope.word.word + ".mp3").getDownloadURL();
Он получает тот же URL-адрес для загрузки, который вы можете получить из консоли Firebase Storage. Этот метод прост, но требует, чтобы вы знали путь к вашему файлу, который в моем приложении составляет около 300 строк кода, для относительно простой структуры базы данных. Если ваша база данных сложна, это будет кошмар. И вы можете загружать файлы из внешнего интерфейса, но это предоставит ваши учетные данные любому, кто загружает ваше приложение. Таким образом, для большинства проектов вы захотите загрузить свои файлы с вашего внутреннего узла или облачных функций Google, затем получить URL-адрес для загрузки и сохранить его в своей базе данных вместе с другими данными о вашем файле.
getSignedUrl() для временных URL-адресов загрузки
getSignedUrl() легко использовать из внутреннего интерфейса узла или из облачных функций Google:
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);
}
});
});
});
});
}
Подписанный URL-адрес для загрузки выглядит следующим образом:
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
Подписанный URL-адрес имеет срок действия и длинную подпись. В документации для командной строки gsutil signurl -d говорится, что подписанные URL-адреса являются временными: срок действия по умолчанию составляет один час, а максимальный срок действия - семь дней.
Я расскажу здесь, что getSignedUrl никогда не говорит, что срок действия подписанного вами URL истекает через неделю. Код документации имеет 3-17-2025
в качестве даты истечения срока действия, предлагая вам установить годы истечения в будущем. Мое приложение работало отлично, а через неделю рухнуло. В сообщении об ошибке говорится, что подписи не совпадают, а URL-адрес загрузки истек. Я сделал различные изменения в своем коде, и все работало... пока все не рухнуло неделю спустя. Это продолжалось более месяца разочарования.
Сделайте ваш файл общедоступным
Вы можете установить права доступа к вашему файлу для публичного чтения, как описано в документации. Это можно сделать из браузера Cloud Storage или с вашего 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));
});
Результат будет выглядеть в вашем браузере облачного хранилища:
![enter image description here]()
Затем любой желающий может использовать стандартный путь для загрузки вашего файла:
https://storage.googleapis.com/languagetwo-cd94d.appspot.com/Audio/English/United_States-OED-0/system.mp3
Еще один способ сделать файл общедоступным - использовать метод makePublic(). Мне не удалось заставить это работать, сложно правильно определить пути к файлам и корзине.
Интересной альтернативой является использование списков контроля доступа. Вы можете сделать файл доступным только пользователям, которых вы включили в список, или использовать authenticatedRead
, чтобы сделать файл доступным для всех, кто вошел в систему из учетной записи Google. Если бы была опция "любой, кто вошел в мое приложение с помощью Firebase Auth", я бы использовал это, так как это ограничило бы доступ только для моих пользователей.
Создайте свой собственный URL для загрузки с помощью firebaseStorageDownloadTokens
Несколько ответов описывают недокументированное свойство объекта Google Storage firebaseStorageDownloadTokens
. При этом вы можете указать Storage токен, который хотите использовать. Вы можете сгенерировать токен с помощью узла узла uuid
. Четыре строки кода, и вы можете создать свой собственный URL-адрес для загрузки, тот же URL-адрес для загрузки, который вы получаете с консоли или getDownloadURL()
. Четыре строки кода:
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);
Вот код в контексте:
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));
});
Это не опечатка - вы должны вкладывать firebaseStorageDownloadTokens
в два слоя metadata:
!
Дуг Стивенсон отметил, что firebaseStorageDownloadTokens
не является официальной функцией Google Cloud Storage. Вы не найдете его ни в одной документации Google, и нет никаких обещаний, что это будет в будущей версии @google-cloud
. Мне нравится firebaseStorageDownloadTokens
, потому что это единственный способ получить то, что я хочу, но у него есть "запах", который его небезопасно использовать.
Почему нет getDownloadURL() от узла?
Как писал @Clinton, Google должен создать метод file.getDownloadURL()
в @google-cloud/storage
(т.е. в вашем внутреннем узле). Я хочу загрузить файл из Google Cloud Functions и получить URL-адрес для загрузки токена.
Ответ 6
Я предлагаю использовать опцию predefinedAcl: 'publicRead'
при загрузке файла с Cloud Storage NodeJS 1.6.x или +:
const options = {
destination: yourFileDestination,
predefinedAcl: 'publicRead'
};
bucket.upload(attachment, options);
Затем получить общедоступный URL-адрес так же просто, как:
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);
});
Ответ 7
Один из методов, которые я использую с успехом, - установить значение UUID v4 в ключе с именем firebaseStorageDownloadTokens
в метаданных файла после завершения загрузки, а затем собрать URL-адрес загрузки, следуя структуре Firebase, используемой для создания этих URL, например:
https://firebasestorage.googleapis.com/v0/b/[BUCKET_NAME]/o/[FILE_PATH]?alt=media&token=[THE_TOKEN_YOU_CREATED]
Я не знаю, насколько "безопасно" использовать этот метод (учитывая, что Firebase может изменить способ генерации URL-адресов загрузки в будущем), но его легко реализовать.
Ответ 8
Для тех, кто задается вопросом, куда должен идти файл службы Firebase Admin SDK serviceAccountKey.json. Просто поместите его в папку функций и разверните, как обычно.
Это все еще озадачивает меня, почему мы не можем просто получить URL-адрес загрузки из метаданных, как в Javascript SDK. Создание URL-адреса, которое в конечном итоге истечет и сохранить его в базе данных, нежелательно.
Ответ 9
Извините, но я не могу опубликовать комментарий к вашему вопросу выше из-за отсутствия репутации, поэтому я включу его в этот ответ.
Сделайте, как указано выше, создав подписанный Url, но вместо использования service-account.json, я думаю, вам нужно использовать serviceAccountKey.json, который вы можете создать (замените YOURPROJECTID соответственно)
https://console.firebase.google.com/project/YOURPROJECTID/settings/serviceaccounts/adminsdk
Пример:
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
})
Ответ 10
Я не могу комментировать ответ, который дал Джеймс Дэниелс, но я думаю, что это очень важно прочитать.
Выдача подписанного URL-адреса Как он это сделал, во многих случаях кажется довольно плохим и, возможно, опасным. Согласно документации Firebase, срок действия подписанного URL истекает через некоторое время, поэтому добавление его в базу данных приведет к появлению пустого URL через определенный период времени.
Возможно, что там неправильно истолкована документация, а срок действия подписанного URL не истекает, что может привести к проблемам с безопасностью. Ключ кажется одинаковым для каждого загруженного файла. Это означает, что, получив URL-адрес одного файла, кто-то может легко получить доступ к файлам, к которым он не имеет доступа, просто зная их имена.
Если бы я не понял этого, я бы хотел исправить. В противном случае кто-то должен, вероятно, обновить вышеуказанное решение. Если я могу ошибаться там
Ответ 11
Это то, что я сейчас использую, это просто и работает без нареканий.
Вам не нужно ничего делать с Google Cloud. Это работает из коробки с 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
Ответ 12
Это работает, если вам нужен только общий файл с простым URL-адресом. Обратите внимание, что это может привести к отмене ваших правил хранения 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);
}
});
Ответ 13
У меня была та же проблема, однако я смотрел на код примера функции firebase вместо README. И ответы в этой теме тоже не помогли...
Вы можете избежать передачи файла конфигурации, выполнив следующие действия:
Перейдите в ваш проект Cloud Console> IAM & admin> IAM, найдите учетную запись службы App Engine по умолчанию и добавьте к этому члену роль создателя маркера служебной учетной записи. Это позволит вашему приложению создавать подписанные общедоступные URL-адреса для изображений.
источник: функция автоматического создания миниатюр README
Ваша роль для движка приложения должна выглядеть следующим образом:
![Cloud Console]()
Ответ 14
Для тех, кто использует Firebase SDK и admin.initializeApp
:
1 - Сгенерируйте закрытый ключ и поместите в папку /functions.
2 - Настройте ваш код следующим образом:
const serviceAccount = require('../../serviceAccountKey.json');
try { admin.initializeApp(Object.assign(functions.config().firebase, { credential: admin.credential.cert(serviceAccount) })); } catch (e) {}
Документация
Try/catch заключается в том, что я использую index.js, который импортирует другие файлы и создает одну функцию для каждого файла. Если вы используете один файл index.js со всеми функциями, у вас должно быть все в порядке с admin.initializeApp(Object.assign(functions.config().firebase, { credential: admin.credential.cert(serviceAccount) }));
,
Ответ 15
Начиная с Firebase 6.0.0 я мог получить доступ к хранилищу напрямую с помощью администратора:
const bucket = admin.storage().bucket();
Поэтому мне не нужно было добавлять служебную учетную запись. Затем установка UUID, как указано выше, сработала для получения URL-адреса firebase.
Ответ 16
Если вы используете предопределенное значение списков управления доступом publicRead, вы можете загрузить файл и получить к нему доступ с очень простой структурой URL:
// Upload to GCS
const opts: UploadOptions = {
gzip: true,
destination: dest, // 'someFolder/image.jpg'
predefinedAcl: 'publicRead',
public: true
};
return bucket.upload(imagePath, opts);
Затем вы можете построить URL следующим образом:
const storageRoot = 'https://storage.googleapis.com/';
const bucketName = 'myapp.appspot.com/'; // CHANGE TO YOUR BUCKET NAME
const downloadUrl = storageRoot + bucketName + encodeURIComponent(dest);
Ответ 17
Это лучшее, что я придумал. Это избыточно, но единственное разумное решение, которое сработало для меня.
await bucket.upload(localFilePath, {destination: uploadPath, public: true});
const f = await bucket.file(uploadPath)
const meta = await f.getMetadata()
console.log(meta[0].mediaLink)
Ответ 18
Я ЧИТАЮ... И Я ПРОСТО ИСПОЛЬЗУЮ ПРИМЕР ИЗ GOOGLE ЭТО ПРЯМО СЕЙЧАС ЕГО РАБОТАЕТ ИДЕАЛЬНО - Я ДУМАЮ, ЧТО ВСЕ ЖДУ НЕДЕЛЮ, КАК ОНИ СКАЗАЛ, ЧТО ЭТО ОЖИДАЕТ... ЧТОБЫ ПРОВЕРИТЬ, ЧТО ПРОИСХОДИТ - КОД ПРАВИЛЬНО ЗДЕСЬ: https://github.com/firebase/functions-samples/tree/master/generate-thumbnail
Ответ 19
Если вы получаете сообщение об ошибке:
Облачные функции Google: require (…) не является функцией
попробуйте это:
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);
.....
Ответ 20
Без signedURL()
с использованием makePublic()
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);
});
});
});