如何将从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) =&gt; 。您不能只在此处键入变量,因为 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 存储之前对其进行压缩?

将图像从图库上传到 Firebase 存储时应用程序崩溃

将图像上传到 Firebase 存储后如何获取图像 uri?

将多个图像存储到 Firebase 并获取 url

如何等待 Firebase 存储图像上传,然后运行下一个函数