使用适用于Firebase的功能在Firebase存储中上传文件后,我想获取该文件的下载网址。
我有这个:
...
return bucket
.upload(fromFilePath, {destination: toFilePath})
.then((err, file) => {
// Get the download url of file
});
目标文件有很多参数。甚至一个名为mediaLink
的人。但是,如果我尝试访问此链接,则会收到此错误:
匿名用户没有storage.objects.get访问对象...
有人可以告诉我如何获取公共下载网址吗?
谢谢
答案 0 :(得分:85)
您需要通过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's public URL
});
您需要使用your service account credentials初始化@google-cloud/storage
,因为应用程序默认凭据是不够的。
更新:现在可以通过Firebase管理SDK访问云端存储SDK,{@ 3}} @ google-cloud / storage。唯一的方法是你要么:
答案 1 :(得分:44)
以下是有关如何在上传时指定下载令牌的示例:
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值将告知云存储将其用作其公共身份验证令牌。
非常感谢@martemorfosis
答案 2 :(得分:14)
随着功能对象响应的最近更改,您可以获得将下载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);
答案 3 :(得分:14)
如果您正在处理Firebase项目,则可以在Cloud Function中创建签名的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服务帐户具有足够的权限来运行此功能
使用普通的Firebase配置时,第一次运行上述代码时,会出现错误项目XXXXXX中尚未使用身份和访问管理(IAM)API,或者该IDM被禁用。。如果您遵循错误消息中的链接并启用IAM API,则会收到另一个错误:需要iam.serviceAccounts.signBlob权限才能对服务帐户my-service-account 执行此操作。添加令牌创建者角色可解决第二个权限问题。
答案 4 :(得分:11)
我成功使用的一种方法是在完成上传后将UUID v4值设置为文件元数据中名为firebaseStorageDownloadTokens
的键,然后按照Firebase结构自行组装下载URL用于生成这些URL,例如:
https://firebasestorage.googleapis.com/v0/b/[BUCKET_NAME]/o/[FILE_PATH]?alt=media&token=[THE_TOKEN_YOU_CREATED]
我不知道多少&#34;安全&#34;是使用这种方法(假设Firebase可能会改变将来生成下载URL的方式),但它很容易实现。
答案 5 :(得分:10)
对于那些想知道Firebase Admin SDK serviceAccountKey.json文件应该去哪里的人。只需将其放在函数文件夹中并照常部署即可。
仍然让我感到困惑的是,为什么我们不能像在Javascript SDK中那样从元数据中获取下载URL。生成最终将过期并将其保存在数据库中的URL是不可取的。
答案 6 :(得分:9)
我建议在使用Cloud Storage NodeJS 1.6.x或+上传文件时使用选项predefinedAcl: 'publicRead'
:
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 :(得分:7)
很抱歉,由于缺少声誉,我无法在上述问题上发表评论,因此我会在此答案中加入。
通过生成签名的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
})
答案 8 :(得分:4)
我无法评论詹姆斯丹尼尔斯给出的答案,但我认为这非常重要。
给出一个签名的网址就像他在许多情况下看起来相当糟糕和可能危险。 根据Firebase的文档,签名的网址会在一段时间后过期,因此将其添加到您的数据库会在一段时间后导致空网址
可能是因为误解了那里的文档而且签名的网址没有过期,结果会出现一些安全问题。 对于每个上传的文件,密钥似乎都是相同的。这意味着一旦你获得了一个文件的网址,就可以通过了解他们的名字轻松访问他无法访问的文件。
如果我错过了理解,那么我将会纠正。 否则有人应该更新上面提到的解决方案。 如果我错了那么
答案 9 :(得分:2)
如果您只需要一个包含简单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);
}
});
答案 10 :(得分:2)
我遇到了同样的问题,但是,我正在查看firebase函数示例的代码,而不是README。对此线程的回答也没有帮助...
您可以通过执行以下操作避免传递配置文件:
转到您项目的Cloud Console > IAM & admin > IAM,找到应用 引擎默认服务帐户并添加服务帐户令牌 该成员的创建者角色。这将允许您的应用创建已签名 图片的公共网址。
来源:Automatically Generate Thumbnails function README
您在应用引擎中的角色应如下所示:
答案 11 :(得分:2)
这是我目前使用的方法,它很简单并且可以完美地工作。
您无需使用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].mediaLinkenter
答案 12 :(得分:1)
没有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);
});
});
});
答案 13 :(得分:1)
此答案将总结在将文件上传到Google / Firebase Cloud Storage时获取下载URL的选项。共有三种下载URL:
有三种获取令牌下载URL的方法。其他两个下载URL只有一种获取它们的方法。
从Firebase存储控制台
您可以从Firebase存储控制台获取下载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()
documentation告诉我们使用getDownloadURL()
:
let url = await firebase.storage().ref('Audio/English/United_States-OED-' + i +'/' + $scope.word.word + ".mp3").getDownloadURL();
与您从Firebase Storage控制台获得的下载URL相同。这种方法很简单,但是要求您知道文件的路径,在我的应用中,该路径为300行代码,以实现相对简单的数据库结构。如果您的数据库很复杂,那将是一场噩梦。您可以从前端上传文件,但这会将您的凭据公开给下载您的应用程序的任何人。因此,对于大多数项目,您需要从Node后端或Google Cloud Functions上传文件,然后获取下载URL并将其与有关文件的其他数据一起保存到数据库中。
用于临时下载URL的getSignedUrl()
getSignedUrl()易于在Node后端或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);
}
});
});
});
});
}
签名的下载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过期。我对代码进行了各种更改,一切正常……直到一周后所有崩溃。持续了一个多月的挫败感。
使文件公开可用
您可以将文件的权限设置为公开读取,如documentation中所述。可以从Cloud Storage Browser或您的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));
});
结果将在您的Cloud Storage Browser中如下所示:
然后任何人都可以使用标准路径下载文件:
https://storage.googleapis.com/languagetwo-cd94d.appspot.com/Audio/English/United_States-OED-0/system.mp3
公开文件的另一种方法是使用方法makePublic()。我无法使其正常工作,正确设置存储桶和文件路径是很棘手的。
一个有趣的替代方法是使用Access Control Lists。您可以使文件仅对列表中的用户可用,或使用authenticatedRead
使文件对从Google帐户登录的任何人可用。如果存在“使用Firebase Auth登录到我的应用程序的任何人”的选项,我将使用此选项,因为它将限制仅对我的用户的访问。
使用firebaseStorageDownloadTokens构建自己的下载URL
几个答案描述了一个未记录的Google Storage对象属性firebaseStorageDownloadTokens
。这样,您可以告诉Storage您想要使用的令牌。您可以使用uuid
节点模块生成令牌。四行代码,您可以构建自己的下载URL,即从控制台或getDownloadURL()
获得的下载URL。四行代码是:
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:
的双层中!
道格·史蒂文森(Doug Stevenson)指出,firebaseStorageDownloadTokens
不是Google Cloud Storage的正式功能。您不会在任何Google文档中找到它,也无法保证它将在@google-cloud
的未来版本中使用。我喜欢firebaseStorageDownloadTokens
,因为它是获得我想要的东西的唯一方法,但是它有“气味”,使用起来不安全。
为什么没有来自Node的getDownloadURL()?
正如@Clinton所写,Google应该在file.getDownloadURL()
中将@google-cloud/storage
用作方法(即您的Node后端)。我想从Google Cloud Functions上传文件并获取令牌下载URL。
答案 14 :(得分:1)
https://stackoverflow.com/users/269447/laurent的回答效果最好
const uploadOptions: UploadOptions = {
public: true
};
const bucket = admin.storage().bucket();
[ffile] = await bucket.upload(oPath, uploadOptions);
ffile.metadata.mediaLink // this is what you need
答案 15 :(得分:1)
对于使用Firebase SDK和admin.initializeApp
的人:
1 - Generate a Private Key并放在/ 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) }));
。
答案 16 :(得分:0)
对于那些试图使用token参数共享文件并想使用gsutil命令的人,这是我的做法:
首先,您需要通过运行gcloud auth
然后运行:
gsutil setmeta -h "x-goog-meta-firebaseStorageDownloadTokens:$FILE_TOKEN"
gs://$FIREBASE_REPO/$FILE_NAME
然后您可以通过以下链接下载文件:
https://firebasestorage.googleapis.com/v0/b/$FIREBASE_REPO/o/$FILE_NAME?alt=media&token=$FILE_TOKEN
答案 17 :(得分:0)
我已经在下面的URL中张贴了我的答案...,您可以在其中获取完整的代码并获得解决方案
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);
答案 18 :(得分:0)
从firebase 6.0.0开始,我可以这样直接通过管理员访问存储:
const bucket = admin.storage().bucket();
因此,我不需要添加服务帐户。然后按照上述设置UUID即可获取Firebase网址。
答案 19 :(得分:0)
如果遇到错误:
Google Cloud Functions: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 :(得分:0)
这是我最好的。这是多余的,但是对我有用的唯一合理的解决方案。
await bucket.upload(localFilePath, {destination: uploadPath, public: true});
const f = await bucket.file(uploadPath)
const meta = await f.getMetadata()
console.log(meta[0].mediaLink)
答案 21 :(得分:0)
如果使用预定义的访问控制列表值“ publicRead”,则可以上传文件并使用非常简单的url结构进行访问:
const App = () => {
return (
<div
style={{
width: "100%",
height: "100%",
margin: "0",
padding: "0"
}}
>
<ParticleComponent />
<div
style={{
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: "100%"
}}
>
<h1>test</h1>
</div>
</div>
);
}
然后您可以像这样构造网址:
// Upload to GCS
const opts: UploadOptions = {
gzip: true,
destination: dest, // 'someFolder/image.jpg'
predefinedAcl: 'publicRead',
public: true
};
return bucket.upload(imagePath, opts);
答案 22 :(得分:-1)
我在管理员存储文档上看到了
const options = {
version: 'v4',
action: 'read',
expires: Date.now() + 15 * 60 * 1000, // 15 minutes
};
// Get a v4 signed URL for reading the file
const [url] = await storage
.bucket(bucketName)
.file(filename)
.getSignedUrl(options);
console.log('Generated GET signed URL:');
console.log(url);
console.log('You can use this URL with any user agent, for example:');
console.log(`curl '${url}'`);