javascript - 从 Firebase 云存储中检索文件时出错
问题描述
我上传了一个包含图像文件和其他一些字段的表单数据。我将文件图像上传到我的 firebase 云存储,并将其他表单数据添加到我的 firebase firestore 集合中,其中包含我上传的文件图像的构造 URL,类似于 ( https://firebasestorage.googleapis.com/v0/b/${storageBucket key})/o/${filename}?alt=media
)。我的上传成功,但是当我尝试使用 URL 在浏览器上查看文件图像时出现错误
{
"error": {
"code": 404,
"message": "Not Found. Could not get object",
"status": "GET_OBJECT"
}
}
下面是我的 RestApi 代码:
exports.addType = (req, res) => {
const BusBoy = require('busboy');
const path = require('path');
const os = require('os');
const fs = require('fs');
const busboy = new BusBoy({headers: req.headers});
let fileName;
let fileToBeUploaded = {};
let fields = {};
busboy.on('field', (fieldname, data)=> {
fields[fieldname] = data;
})
busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
const imageExtension = filename.split('.')[filename.split('.').length - 1];
fileName = `${Math.round(Math.random() * 10000000000000)}.${imageExtension}`;
const filepath = path.join(os.tmpdir(), fileName);
fileToBeUploaded = { filepath, mimetype };
file.pipe(fs.createWriteStream(filepath));
})
busboy.on('finish', ()=> {
let imageUrl;
admin.storage().bucket().upload(fileToBeUploaded.filepath, {
destination:`Type/${fileName}`,
resumable: false,
metadata: {
metadata: {
contentType: fileToBeUploaded.mimetype
}
}
})
.then(() => {
imageUrl = `https://firebasestorage.googleapis.com/v0/b/${config.storageBucket}/o/${fileName}?alt=media`;
return db.collection('Type').doc(fields.type);
})
.then(data => {
if(data.exists){
return res.status(400).json({error: `${fields.type} already exist`})
}
db.collection('Type').doc(fields.type).set({
type: fields.type,
category: fields.category,
details: fields.details,
imageUrl
})
})
.then(()=> {
return res.status(200).json({message: `${fields.type} has been added`});
})
.catch(err => {
return res.status(500).json({error: err.code});
})
})
busboy.end(req.rawBody);
}
解决方案
是的。这是 Firebase 存储中的一个错误。访问(在安全规则下)需要“授权”标头 - HTML 和浏览器不支持为<img>
标签添加标头。可以通过以下方式访问此类文件:
- 使用绕过安全规则的“长期”(即永久)URL
ref.getDownloadURL()
(对于需要成为 SEO 结果的一部分的图像有用 - 完全公开) - 或者
- 在添加适当的标头时使用异步获取(获取或 axios 或 XMLHttpRequest 或其他)
authorization:
。标头的正确值是多少?在 Google 的 Github 中进行了一些研究,但事实证明它就像`Firebase ${JWTToken}`
(注意空格)一样简单。 - 这个gitHub 问题详细讨论了它,但目前的解决方案是:
提议的浏览器/节点端解决方案
由于浏览器的限制,这不是最直接的。无法将自定义标头直接添加到<img>
标签,但可以设置onError
处理程序以进行独立提取。
JSX
<img
src={image}
//crossOrigin="use-credentials"
style={whatever}
alt={whatever}
onError={errorHandler}
/>
其中:{image} 是 Firebase 存储 URL,可以完全以编程方式生成。注意:URL必须包含alt=media
标签(或适合文件类型的任何内容)。如果缺少此参数,Firebase 存储将返回元数据 JSON 对象。{errorHandler} 见下文
Javascript
//built in React
const [authorization, setAuthorization] = useState(null);
//Asynchronously fetches users JWT. Note closure, as useEffect itself must be synchronous
useEffect(() => {
if (user) {
(async () => {
const authToken = await fetchJWT();
setAuthorization(authToken);
})();
}
}, [user]);
//Technically, this is in my wrapper library - uses the Firebase Auth service to fetch a user's JWT
export const fetchJWT = async (user) => {
const thisUser = user || FirebaseAuth.currentUser;
//the "true" below forces a reset
const JWT = await thisUser.getIdToken(true);
return JWT;
}
//More-or-less copied from firebase-js-sdk/storage/src/implementation/requests.ts
const addAuthHeader_ = async (headers) => {
if (authorization !== null && authorization.length > 0) {
headers["Authorization"] = "Firebase " + authorization;
}
};
// handles the actual error event
const errorHandler = async (e) => {
e.stopPropagation();
const target = e.target;
const src = $(target).attr("src");
let headers = {};
addAuthHeader_(headers);
const options = {
headers: headers
};
await fetch(src, options)
.then((res) => {
return res.status === 200 && res.blob();
})
.then((blob) => {
if (!blob) {
return;
}
$(target).prop("src", URL.createObjectURL(blob));
});
};
解释
当<img>
标签遇到错误时,errorHandler
src
从事件目标中获取属性。- 然后它为调用创建一个
options
对象fetch()
,包括标题。标authorization
头添加了一个标头,该标头由一个字符串组成`Firebase ${authortization}`
- 获取的结果作为 Blob 保存到本地存储,并使用创建的 URL
URL.createObjectURL(blob)
,然后将其设置为src
目标的<img>
。
结果
遵循其中设置的所有安全规则,从 Firebase 存储中获取文件(在本例中为图像) 。不需要永久令牌。只有经过身份验证的用户才能访问。(我会注意到我对新用户使用匿名用户帐户,并对其访问设置了适当的限制)
往前走
我可能会向<img>
旨在显示 Firebase 存储中的图像或可能检查 URL 的元素添加一个类。这样,可以指定一个 errorHandler 来处理所有这些。
推荐阅读
- javascript - 将 Fabric 项目从 1.6.3 升级到 3.6.x
- java - sql映射到json对象
- java - 接受用户输入时 Java Scanner 出现奇怪错误
- python - 如何使用子进程将 Windows nslookup 的输出过滤为仅显示名称?
- python - python中巴恩斯利蕨类植物的问题
- testing - 模拟 axios 请求 nuxt
- c - 调用函数会改变另一个指针的值
- java - 保存的数据与数据库内容不对应 SQLite litepal
- python - Django rest框架,在序列化程序中创建用户字段时出错
- php - 如何在 wordpress 中的购物车后定位我的插件