如何将从firebase存储上传的图像保存到firestore数据库中的currentUser
Posted
技术标签:
【中文标题】如何将从firebase存储上传的图像保存到firestore数据库中的currentUser【英文标题】:How to save image uploaded from firebase storage to currentUser in firestore database 【发布时间】:2022-01-06 15:29:48 【问题描述】:我不确定如何获取上传到 firebase 存储的图片 url 并将其保存到 firestore 数据库。目前我有一个配置文件组件,用于存储用户将照片上传到 Firebase 存储的逻辑。我有一个监听用户更改的 UserContext 文件。以及创建用户的 Firebase.utils。
Firebase.utils 文件:
import firebase from 'firebase/compat/app';
import 'firebase/compat/firestore';
import 'firebase/compat/auth';
import "firebase/compat/storage";
const config =
apiKey: "",
authDomain: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: "",
measurementId: ""
;
firebase.initializeApp(config);
export const createUserProfileDocument = async (userAuth, additionalData) =>
// If user is not signed in do nothing
if (!userAuth) return;
const userRef = firestore.doc(`users/$userAuth.uid`)
const snapShot = await userRef.get()
if (!snapShot.exists)
// Added photoURL @@@
const displayName, email, photoURL = userAuth;
const createdAt = new Date();
try
await userRef.set(
displayName,
email,
createdAt,
photoURL,
...additionalData
)
catch (error)
console.log('error creating user', error.message)
return userRef;
export const auth = firebase.auth();
export const firestore = firebase.firestore();
const storage = firebase.storage();;
export storage, firebase as default ;
UserContext 文件:import React, useContext, useState, useEffect from 'react';
import auth, createUserProfileDocument from "../Firebase/Firebase.utils";
const UserContext = React.createContext(null);
const UserUpdateContext = React.createContext();
const UserUpdateNameContext = React.createContext();
const UserUpdateEmailContext = React.createContext();
export const useUserContext = () =>
// useContext hook
return useContext(UserContext);
export const useUserContextUpdate = () =>
// useContext hook - toggleUser signout function
return useContext(UserUpdateContext)
export const useUserNameUpdate = () =>
// useContext hook - update user displayName
return useContext(UserUpdateNameContext)
export const useUserEmailUpdate = () =>
// useContext hook - update user email
return useContext(UserUpdateEmailContext)
export const UserContextProvider = ( children ) =>
const [currentUser, setUser] = useState(null);
let unsubscribeFromAuth = null;
console.log(currentUser)
useEffect(() =>
unsubscribeFromAuth = auth.onAuthStateChanged(async userAuth =>
if (userAuth)
const userRef = await createUserProfileDocument(userAuth);
userRef.onSnapshot(snapShot =>
setUser(
id: snapShot.id,
...snapShot.data()
);
);
else
setUser(null)
// setUser( currentUser: userAuth ) OBJECTS ARE TRUTHY
);
return () =>
unsubscribeFromAuth();
;
, [])
console.log(unsubscribeFromAuth)
const toggleUser = () =>
auth.signOut()
.then(() =>
setUser(null)
)
.catch(e => console.log('There was a error:'(e)))
// console.log(currentUser)
// Get current window width
const useWindowWidth = () =>
const [width, setWidth] = useState(window.innerWidth)
useEffect(() =>
const handleResize = () => setWidth(window.innerWidth)
window.addEventListener('resize', handleResize)
return () =>
window.removeEventListener('resize', handleResize)
)
return width
const width = useWindowWidth();
// Slice off end of displayName if reaches a certain length
const sliceDisplayName = (currentUser) =>
if (currentUser)
const displayName = currentUser.displayName;
return (
width >= 1441 ? displayName.substring(0, 16) + '...'
: width <= 1440 && width >= 769 ? displayName.substring(0, 14) + '...'
: width <= 768 ? displayName.substring(0, 7) + '...'
: displayName
)
else (console.log("No user found :("))
// console.log(sliceDisplayName(currentUser))
// Slice off end of email if reaches a certain length
const sliceEmail = (currentUser) =>
if (currentUser)
const email = currentUser.email;
return (
width >= 1441 ? email.substring(0, 16) + '...'
: width <= 1440 && width >= 769 ? email.substring(0, 14) + '...'
: width <= 768 ? email.substring(0, 7) + '...'
: email
)
else (console.log("No user found :("))
// console.log(sliceEmail(currentUser))
return (
<UserContext.Provider value=currentUser >
<UserUpdateContext.Provider value=toggleUser >
<UserUpdateNameContext.Provider value=sliceDisplayName >
<UserUpdateEmailContext.Provider value=sliceEmail >
children
</UserUpdateEmailContext.Provider >
</UserUpdateNameContext.Provider >
</UserUpdateContext.Provider >
</UserContext.Provider >
)
;
配置文件组件:import React, useState from 'react';
import ReactTooltip from 'react-tooltip';
import useUserContext, useUserContextUpdate, useUserNameUpdate from '../../Utilities/Context/UserContext';
import storage from "../../Utilities/Firebase/Firebase.utils";
import ref, uploadBytesResumable, getDownloadURL from "firebase/storage";
import ActivityFeed from '../../../src/components/ActivityFeed/ActivityFeed';
import Post from '../../../src/components/Post/Post';
import './Profile.css';
const Profile = ( imageDate ) =>
const currentUser = useUserContext(); // Current user
const sliceDisplayName = useUserNameUpdate(); // Window width < (width) ? update displayName length
const [image, setImage] = useState("");
const [url, setUrl] = useState("");
// Listen for state changes, errors, and completion of the upload.
const handleUpload = () =>
const uploadTask = storage.ref(`images/$image.name`).put(image);
uploadTask.on('state_changed',
(snapshot) => console.log(snapshot) ,
(error) =>
switch (error.code)
case 'storage/unauthorized':
break;
case 'storage/canceled':
break;
case 'storage/unknown':
break;
,
(e) =>
storage
.ref("images")
.child(image.name)
.getDownloadURL(uploadTask.snapshot.ref)
.then(url =>
setUrl(url);
console.log('File available at', url);
)
);
console.log("image: ", image);
console.log(image.lastModifiedDate) // Image date uploaded. image.lastmodifiedDate === undefined ?
console.log(url)
const handleUploadChange = e => // Maybe inside this function i can do the logic for recent activity and 0 photos +1
if (e.target.files[0])
setImage(e.target.files[0]);
;
return (
<div className="container-flex">
<div className="top-content-container w-100">
<div className="bot-content-wrapper px-1 py-2 mx-lg-auto d-flex flex-column flex-lg-row">
<div className="w-spacer profile-image-wrapper position-relative">
currentUser ?
<input
type="file"
for="Upload Image"
accept="image/*"
name="image"
id="file"
onChange=handleUploadChange
onClick=handleUploadChange
style= display: "none"
/>
: ''
<label for="file">
url.length <= 0 ?
<img
id='myimg'
className="profile-image-default profile-image d-block"
/>
: currentUser ?
<>
<img
id='myimg'
className="profile-image d-block"
src=url
data-tip="Click me to update profile picture!" />
<ReactTooltip place="top" type="dark" effect="float" />
</>
:
<img
id='myimg'
className="profile-image-default profile-image d-block"
/>
</label>
</div>
<div className="d-flex flex-column flex-lg-row align-items-lg-center w-lg-75 m-l-4">
<div className="d-flex flex-column flex-lg-row ml-auto pr-1 m-r-md-vw">
<div className="m-r-md">
<div className="d-flex flex-column w-100 m-r-6">
<div>
currentUser ?
<h2
data-tip=currentUser.displayName>
sliceDisplayName(currentUser)
<span><ReactTooltip place="top" type="dark" effect="float" /></span>
</h2>
:
<h2>No User</h2>
</div>
<div className="d-flex flex-column flex-lg-row">
<div className="">
<i className="bi bi-people"></i>
<span className="banner-list-font mx-1">0 friends</span>
</div>
<div className="mx-lg-2">
<i className="bi bi-star"></i>
<span className="banner-list-font mx-1">0 reviews</span>
</div>
<div className="">
<i className="bi bi-camera"></i>
<span className="banner-list-font mx-1">0 photos</span>
</div>
</div>
</div>
</div>
<hr className=" d-lg-none" style= color: '#0a0a0a' ></hr>
<div className="ml-3">
<div className="update-profile-wrapper grey-line-break d-flex flex-column m-l">
<div className="">
image.name !== undefined ?
<button
className="banner-list-font"
onClick=handleUpload
// onClick=() =>
// handleUpload();
// forceUpdate();
//
>Add Profile Photo
</button>
: ''
</div>
<div className="">
<a className="banner-list-font" href='#'>Update Your Profile</a>
</div>
<div className="">
<a className="banner-list-font" href='#'>Find Friends</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div className="bot-content-container px-1 py-4 custom-padding">
<div className="bot-content-wrapper mx-lg-auto d-flex flex-column flex-lg-row">
<div className="sidebar d-flex flex-column mx-auto mx-lg-0 mt-lg-5 py-lg-2 px-2">
currentUser ?
<h4 className="mb-3">currentUser.displayName</h4>
:
<h4 className="mb-3">No User</h4>
<ul className="p-0">
<a className="cursor-pointer text-decoration-none">
<li className="d-flex flex-row sidebar-item sidebar-list-font">
<i className="mx-2 bi bi-person-badge"></i>
<h5 className="sidebar-list-font">Overview</h5>
</li>
<hr style= color: '#0a0a0a' ></hr>
</a>
<a className="cursor-pointer text-decoration-none">
<li className="d-flex flex-row sidebar-item sidebar-list-font">
<i className="mx-2 bi bi-person-plus"></i>
<h5 className="sidebar-list-font">Friends</h5>
</li>
<hr style= color: '#0a0a0a' ></hr>
</a>
<a className="cursor-pointer text-decoration-none">
<li className="d-flex flex-row sidebar-item sidebar-list-font">
<i className="mx-2 bi bi-award mx-1"></i>
<h5 className="sidebar-list-font">Reviews</h5>
</li>
<hr style= color: '#0a0a0a' ></hr>
</a>
<a className="cursor-pointer text-decoration-none">
<li className="d-flex flex-row sidebar-item sidebar-list-font">
<i className="mx-2 bi bi-lightbulb"></i>
<h5 className="sidebar-list-font">Tips</h5>
</li>
<hr style= color: '#0a0a0a' ></hr>
</a>
<a className="cursor-pointer text-decoration-none">
<li className="d-flex flex-row sidebar-item sidebar-list-font">
<i className="mx-2 bi bi-bookmark-star"></i>
<h5 className="sidebar-list-font">Bookmarks</h5>
</li>
<hr style= color: '#0a0a0a' ></hr>
</a>
<a className="cursor-pointer text-decoration-none">
<li className="d-flex flex-row sidebar-item sidebar-list-font">
<i className="mx-2 bi bi-bookmarks"></i>
<h5 className="sidebar-list-font">Collections</h5>
</li>
<hr style= color: '#0a0a0a' ></hr>
</a>
<a className="cursor-pointer text-decoration-none">
<li className="d-flex flex-row sidebar-item sidebar-list-font">
<i className="mx-2 bi bi-calendar-check"></i>
<h5 className="sidebar-list-font">Events</h5>
</li>
<hr style= color: '#0a0a0a' ></hr>
</a>
<a className="cursor-pointer text-decoration-none">
<li className="d-flex flex-row sidebar-item sidebar-list-font">
<i className="mx-2 bi bi-clock-history"></i>
<h5 className="sidebar-list-font">Order History</h5>
</li>
<hr style= color: '#0a0a0a' ></hr>
</a>
</ul>
</div>
<div className="d-flex flex-column flex-lg-row w-100-md w-75-lg p-3 p-lg-0 m-l-4 pt-lg-3 pt-xl-4">
<div className="activity m-l-3">
<h3 className="heading-red">Notifications</h3>
<p className="font-14">No new friend requests or compliments at this time.</p>
<hr className="d-none d-lg-block" style= color: '#0a0a0a' ></hr>
<h3 className="heading-red">Recent Activity</h3>
<ActivityFeed />
<Post />
</div>
<hr className="d-lg-none" style= color: '#0a0a0a' ></hr>
<div className="grey-line-break ml-3">
<h3 className="heading-red mb-1 break-word">About
currentUser ?
<h3
data-tip=currentUser.displayName
className="heading-red mb-1">
sliceDisplayName(currentUser)
<span><ReactTooltip place="top" type="dark" effect="float" /></span>
</h3>
:
<h3 className="heading-red mb-1">No User</h3>
</h3>
<h5 className="about-subHeading mt-2">Yelping Since</h5>
<p className="font-14">Some month</p>
<h5 className="about-subHeading mt-2">Things I Love</h5>
<p className="font-14">You haven't said yet...</p>
</div>
</div>
</div>
</div>
</div>
);
;
export default Profile;
**编辑**
更新了上传任务
import createUserProfileDocument from "../../Utilities/Firebase/Firebase.utils";
() =>
storage
.ref('images/')
.child(image.name)
.getDownloadURL(uploadTask.snapshot.ref)
.then(async (url, firestore, userAuth) =>
setUrl(url);
const userRef = firestore.doc(`users/$userAuth.uid`)
await userRef.update(
photoURL: url
);
console.log('File available at', url);
)
更新后出错:
未处理的拒绝(TypeError):无法读取未定义的属性(读取“文档”)
**编辑更新 #2**
在 Frank van Puffelen 的帮助下,我似乎设法从存储中获取上传的照片进入 firestore 数据库。我可以在 photoURL 中当前用户的 console.log 中看到它,它也显示在 firebase db 用户集合中。但是,如果我刷新或转到另一个页面,图像就会消失,即使刷新后它仍然在 currentUser console.log 中显示 url。为什么会这样?一定是因为 setUrl(url) 在每次渲染时都会将 url 图像的状态重新初始化为空。我应该很好地放弃这一切并直接从 currentUser 调用图像 url,例如: currentUser.photoURL
更新代码:import storage, firestore from "../../Utilities/Firebase/Firebase.utils";
() =>
storage
.ref('images/')
.child(image.name)
.getDownloadURL(uploadTask.snapshot.ref)
.then(async (url) =>
setUrl(url);
const userRef = firestore.doc(`users/$currentUser.id`)
await userRef.update(
photoURL: url
);
console.log('File available at', url);
)
【问题讨论】:
【参考方案1】:据我所知,您上传图片并在此 sn-p 中获取其下载 URL:
storage
.ref("images")
.child(image.name)
.getDownloadURL(uploadTask.snapshot.ref)
.then(url =>
setUrl(url);
console.log('File available at', url);
)
要将新 URL 写入 Firestore 中的用户配置文件文档,请将其添加到 then()
回调:
const userRef = firestore.doc(`users/$userAuth.uid`)
await userRef.update(
photoURL: url
);
【讨论】:
感谢您回答弗兰克,我更新了问题以反映这一点...我收到了错误回复(请参阅更新后的问题)。 这不行:.then(async (url, firestore, userAuth) =>
。您不能只在此处键入变量,因为 getDownloadURL
没有传递这些值。您需要找到另一种方法来访问 firestore
和其中的 UID:const firestore = firebase.firestore();
和 const uid = firebase.auth().currentUser.uid;
。
所以为了清楚起见,我现在可以放弃 [url, setUrl] = useState("") 并使用 currentUser.photoURL 调用用户图像。 url,setUrl 导致图像在每次重新渲染时消失,因为状态正在初始化为空,对吗?
如果你想更新本地 React 状态,你需要useState
。如果您有用户的 Firestore 文档的实时侦听器,您还可以从该侦听器更新 UI,但我没有立即在您的代码中看到这一点。 --- 请注意,您的原始问题(“如何将从 firebase 存储上传的图像保存到 firestore 数据库中的 currentUser”)似乎已解决,因此我建议您转到一个新问题并为此设置一个新的单独的 MCVE .
我相信 useEffect 钩子监听 auth.onAuthStateChanged(async userAuth => in UserContext file ?以上是关于如何将从firebase存储上传的图像保存到firestore数据库中的currentUser的主要内容,如果未能解决你的问题,请参考以下文章
如何在单个事务中将图像上传到 Firebase 存储并在数据库中保存参考?
如何在将图像上传到 Firebase 存储之前对其进行压缩?