我的 firebase 存储中的下载 URL 没有传输到数据库

My download URL in my firebase storage is not transferring to the database

我正在使用 React 和 Firebase。我正在尝试将图像的下载 url 存储到名为 'image' 的状态,然后使用该状态添加到正在创建的 post 中的 'imageurl' 字段。 更新图像状态似乎没有问题,但是当我查看在我的 firebase 数据库中创建的新 post 对象中的 'imageurl' 字段时,它是空的。这是代码:

import { storage, db } from '../firebase-config';
import { useState } from 'react';
import { ref, getDownloadURL, uploadBytesResumable } from "firebase/storage";
import { collection, addDoc } from 'firebase/firestore';
import { format } from 'date-fns';
const CreatePost = () => {

    const [caption, setCaption] = useState('');
    const [progress, setProgress] = useState(0);
    const [image, setImage] = useState('');

    const postsCollectionRef = collection(db, 'Posts');

    const formHandler = (e) => {
      e.preventDefault();
      const file = e.target[0].files[0];
      uploadFiles(file);
      createPost();
    };
  
    const uploadFiles = (file) => {
      if (!file) return;
      const storageRef = ref(storage, `files/${file.name}`);
      const uploadTask = uploadBytesResumable(storageRef, file);
  
      uploadTask.on(
        "state_changed",
        (snapshot) => {
          const prog = Math.round(
            (snapshot.bytesTransferred / snapshot.totalBytes) * 100
          );
          setProgress(prog);
        },
        (error) => console.log(error),
    
        () => {
            getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
              setImage(downloadURL);
            });
        }
      );
    };

    const createPost = async () => {
        const datetime = format(new Date(), 'MMMM dd, yyyy');
        const post = await addDoc(postsCollectionRef, {
            caption: caption,
            imageurl: image,
            date: datetime
        })
        setCaption('');
    }
  
    return (
      <div>
        <form onSubmit={formHandler}>
          <input type="file" className="input" />
          <button type="submit">Upload</button>
          <input value={caption} type='text' placeholder='caption' onChange={(e) => setCaption(e.target.value)} />
        </form>
        <hr />
        <h2>Uploading done {progress}%</h2>
        <img src={image} />
        <p>{image}</p>
      </div>
    );    
}
export default CreatePost;

您应该使用状态来触发对 UI 的更新,并使用其他同步原语来确定何时更新数据库。


例如,在最简单的情况下,您可以在下载 URL:

时从嵌套侦听器中调用 createPost
const formHandler = (e) => {
  e.preventDefault();
  const file = e.target[0].files[0];
  uploadFiles(file);
  // createPost(); //  don't call this here
};

const uploadFiles = (file) => {
  if (!file) return;
  const storageRef = ref(storage, `files/${file.name}`);
  const uploadTask = uploadBytesResumable(storageRef, file);

  uploadTask.on(
    "state_changed",
    (snapshot) => {
      const prog = Math.round(
        (snapshot.bytesTransferred / snapshot.totalBytes) * 100
      );
      setProgress(prog);
    },
    (error) => console.log(error),

    () => {
        getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
          setImage(downloadURL);
          createPost(); //  but instead call it here
        });
    }
  );
};

您可能需要将 downloadURL 和其他要写入数据的值传递到 createPost 调用中,这是不使用 React 的状态来管理数据库更新的另一种情况。


或者,您可以使用 Promises and/or async/await 来同步调用。

const formHandler = (e) => {
  e.preventDefault();
  const file = e.target[0].files[0];
  const url = await uploadFiles(file); //  wait for upload to be done
  createPost(url); //  only then create the post with the url
};

                           //  this function is asynchronous, meaning it returns a promise
const uploadFiles = (file) => async {
  if (!file) return;
  const storageRef = ref(storage, `files/${file.name}`);
  const uploadTask = uploadBytesResumable(storageRef, file);

  uploadTask.on(
    "state_changed",
    (snapshot) => {
      const prog = Math.round(
        (snapshot.bytesTransferred / snapshot.totalBytes) * 100
      );
      setProgress(prog);
    },
    (error) => console.log(error)
  );
  await uploadTask; //  uploadTask is a promise itself, so you can await it
  
  let downloadURL = await getDownloadURL(uploadTask.snapshot.ref);
                 //  getDownloadURL returns a promise too, so... yay more await

  return downloadURL; //  return the URL to the caller
};

文件上传需要一些时间。所以最好在上传完成后调用create post。

    import { storage, db } from '../firebase-config';
    import { useState } from 'react';
    import { ref, getDownloadURL, uploadBytesResumable } from "firebase/storage";
    import { collection, addDoc } from 'firebase/firestore';
    import { format } from 'date-fns';
    const CreatePost = () => {
    
        const [caption, setCaption] = useState('');
        const [progress, setProgress] = useState(0);
        // const [image, setImage] = useState('');  if still need the state for some reason 
    
        const postsCollectionRef = collection(db, 'Posts');
    
        const formHandler = (e) => {
          e.preventDefault();
          const file = e.target[0].files[0];
          uploadFiles(file);
        };
      
        const uploadFiles = (file) => {
          if (!file) return;
          const storageRef = ref(storage, `files/${file.name}`);
          const uploadTask = uploadBytesResumable(storageRef, file);
      
          uploadTask.on(
            "state_changed",
            (snapshot) => {
              const prog = Math.round(
                (snapshot.bytesTransferred / snapshot.totalBytes) * 100
              );
              setProgress(prog);
            },
            (error) => console.log(error),
        
            () => {
              getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => { createPost(downloadURL);
                });
            }
          );
        };
    
        const createPost = async (imageUrl) => {
         // setImage(imageURL); if still need the state for some reason 
            const datetime = format(new Date(), 'MMMM dd, yyyy');
            const post = await addDoc(postsCollectionRef, {
                caption: caption,
                imageurl: imageUrl,
                date: datetime
            })
            setCaption('');
        }
      
        return (
          <div>
            <form onSubmit={formHandler}>
              <input type="file" className="input" />
              <button type="submit">Upload</button>
              <input value={caption} type='text' placeholder='caption' onChange={(e) => setCaption(e.target.value)} />
            </form>
            <hr />
            <h2>Uploading done {progress}%</h2>
            <img src={image} />
            <p>{image}</p>
          </div>
        );    
    }
    export default CreatePost;

或者只需更改代码中的一件事,它也可以正常工作,因为上传功能具有异步操作,您在完成上传后调用 createpost 并将下载 url 传递给createPost 像这样

import { storage, db } from '../firebase-config';
import { useState } from 'react';
import { ref, getDownloadURL, uploadBytesResumable } from "firebase/storage";
import { collection, addDoc } from 'firebase/firestore';
import { format } from 'date-fns';
const CreatePost = () => {
   
    const [image, setImage] = useState('');

    const [caption, setCaption] = useState('');
    const [progress, setProgress] = useState(0);

    const postsCollectionRef = collection(db, 'Posts');

    const formHandler = (e) => {
      e.preventDefault();
      const file = e.target[0].files[0];
      uploadFiles(file);
    };
  
    const uploadFiles = (file) => {
      if (!file) return;
      const storageRef = ref(storage, `files/${file.name}`);
      const uploadTask = uploadBytesResumable(storageRef, file);
  
      uploadTask.on(
        "state_changed",
        (snapshot) => {
          const prog = Math.round(
            (snapshot.bytesTransferred / snapshot.totalBytes) * 100
          );
          setProgress(prog);
        },
        (error) => console.log(error),
    
        () => {
            getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
              setImage(downloadURL)
              createPost(downloadURL)
            });
        }
      );
    };

    const createPost = async (url) => {
        const datetime = format(new Date(), 'MMMM dd, yyyy');
        const post = await addDoc(postsCollectionRef, {
            caption: caption,
            imageurl: url,
            date: datetime
        })
        setCaption('');
    }
  
    return (
      <div>
        <form onSubmit={formHandler}>
          <input type="file" className="input" />
          <button type="submit">Upload</button>
          <input value={caption} type='text' placeholder='caption' onChange={(e) => setCaption(e.target.value)} />
        </form>
        <hr />
        <h2>Uploading done {progress}%</h2>
        <img src={image} />
        <p>{image}</p>
      </div>
    );    
}
export default CreatePost;