How to do image upload with firebase in react.

Tallan Groberg - Nov 30 '19 - - Dev Community

As a web developer, giving your user's image uploading capability is a right a passage. I'm writing this article because of how frustrating it was for me to do this in my e-commerce site and I hope this will come in handy for a react dev out there trying to make it happen.

almost all e-commerce and social media websites require users to be able to upload image files so it's essential

Today we are going to learn how to enable our users to upload single images in react using firebase as a storage API.

Prerequisites:

  1. form handling in react with hooks

  2. basic understanding of npm packages.

  3. a firebase account

5.optional: unix command line. Ill be using it for this tutorial.

overview: we are going to make a form that will take a file and save it to state. upload the file to firebase storage, firebase will give us a custom URL for that image and we will save that image to an object with a key, value pair. a title and an imgUrl, the imgUrl will be a string provided to us from firebase storage.

go to firebase console and make a new project. if you have a firebase account your console page should look something like this.

Alt Text

Click on the add project and click my first project. then you will be able to type your own project name

Alt Text

then you will be able to type your own project name

Alt Text
name it whatever you want

In the second part of the process you will be able to opt into google analytics. click continue with the preferences you decide.

if you do opt into the analytics then choose the default account.

now you should be on your firebase landing page. On the left side of the screen click storage.

Alt Text

inside storage click on get started. you will see a block of code like this.

Alt Text

we will make a place in our react project this code will not be a part of that. this is strictly firebase side code.

if you read the text you will notice that it is configured to upload with an authenticated user. since we are doing this without auth for sake of brevity, click next.

choose a storage location. Ideally this location should be where your users are most likely to use your project.

Alt Text

after you choose a location, a default bucket should be created to store your images.

in the bucket storage page that was just created, go to your storage bucket rules.

Alt Text

now we see the code from earlier. lets change that code to work without auth. __this is a crucial part of getting this to work!!!!!!!!!!!

change it from this.

Alt Text
you have to use firebase auth for this code to work.

to

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {

//this is the part we changed...
      allow read, write: if true;
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

you should be prompted to publish your changes. I know it can take up to a day or so to take effect. That's why we are doing this first in the tutorial.

It may happen sooner but it took firebase a while for me.

the next step is getting a webSDK for our project so lets register this app.

go to the project overview in the top left corner in the navbar. From that page register this as a web app. and give the app a nickname.

if you scroll down you should see an SDK page.

like this:


<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/7.5.0/firebase-app.js"></script>

<!-- TODO: Add SDKs for Firebase products that you want to use
     https://firebase.google.com/docs/web/setup#available-libraries -->
<script src="https://www.gstatic.com/firebasejs/7.5.0/firebase-analytics.js"></script>

<script>
  // Your web app's Firebase configuration
  var firebaseConfig = {
    apiKey: "super secret keys.....asgvegxgevergfvr",
    authDomain: "tallans-imageupload-tutorial.firebaseapp.com",
    databaseURL: "https://tallans-imageupload-tutorial.firebaseio.com",
    projectId: "tallans-imageupload-tutorial",
    storageBucket: "tallans-imageupload-tutorial.appspot.com",
    messagingSenderId: "super secret keys.....asgvegxgevergfvr",
    appId: "super secret app id....adsfa;lsdkjf",
    measurementId: "super secret as;dlkfjal;dskjf"
  };
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);
  firebase.analytics();
</script>

Enter fullscreen mode Exit fullscreen mode

leave this page open and we will come back to it, this is the firebase side configs that we need to get started. we will make a place for firebase in our react app.

lets make a react app.

create-react-app firebase-imageupload
Enter fullscreen mode Exit fullscreen mode

open this in your favorite text editor. It's not required but I will be using vs code.

enter the project directory and make sure everything is working.

cd firebase-imageupload && npm start
Enter fullscreen mode Exit fullscreen mode

you should see the boilerplate web page that react comes with.

Alt Text

make your react app a blank by changing the App.js to this.

import React from 'react';
import './App.css';

function App() {
  return (
    <div className="App">
      blank and ready for image upload. 
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

now install the firebase npm package.

npm i firebase
Enter fullscreen mode Exit fullscreen mode

let's make a directory for the firebase config on the react side. add an index.js file to that directory

mkdir src/firebase && touch src/firebase/firebase.js

Enter fullscreen mode Exit fullscreen mode

add the imports at the top of your firebase.js file.

import firebase from 'firebase/app'
import 'firebase/storage'
Enter fullscreen mode Exit fullscreen mode

below the imports add the firebase SDK.

firebase.js

 var firebaseConfig = {
    apiKey: "super secret keys.....asgvegxgevergfvr",
    authDomain: "tallans-imageupload-tutorial.firebaseapp.com",
    databaseURL: "https://tallans-imageupload-tutorial.firebaseio.com",
    projectId: "tallans-imageupload-tutorial",
    storageBucket: "tallans-imageupload-tutorial.appspot.com",
    messagingSenderId: "super secret keys.....asgvegxgevergfvr",
    appId: "super secret app id....adsfa;lsdkjf",
    measurementId: "super secret as;dlkfjal;dskjf"
  };
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);
//analytics is optional for this tutoral 
  firebase.analytics();

Enter fullscreen mode Exit fullscreen mode

below the firebase.initializeApp(firebaseConfig); add initialize the storage as a variable

const storage = firebase.storage()

Enter fullscreen mode Exit fullscreen mode

at the bottom of the file lets export everything together.


export  {
   storage, firebase as default
 }

Enter fullscreen mode Exit fullscreen mode

now we have a way of using firebase storage functionality through the whole react app.

go to the App.js and lets make the form to get the file and an input field with a type='file'

App.js

//add useState for handling the image as a file and then the image as a url from firebase
import React, {useState} from 'react'
import {storage} from "./firebase/firebase"
//add import for storage 
function App() {
  return (
    <div className="App">
//form for handling file upload
      <form>
        <input 
// allows you to reach into your file directory and upload image to the browser
          type="file"
        />
      </form>

    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

now between the function app() { and return (

Add the the useState's

const allInputs = {imgUrl: ''}
    const [imageAsFile, setImageAsFile] = useState('')
    const [imageAsUrl, setImageAsUrl] = useState(allImputs)
Enter fullscreen mode Exit fullscreen mode

We are set up to add things to the form for the image. Such as a title, comment, post, description ect.. we will see how after the image upload is functional.

It may seem strange to add the image url as a object key: value, pair and that we are uploading a file to a string but this is how I got it working and if anyone has a better way please point to that resource in the comments.

now we need to make the function to handle the image upload as a file so that we can stage it for a post request to firebase.

console.log(imageAsFile)
 const handleImageAsFile = (e) => {
      const image = e.target.files[0]
      setImageAsFile(imageFile => (image))
  }

Enter fullscreen mode Exit fullscreen mode

then add the function to the input field

 <input 
   type="file"
   onChange={handleImageAsFile}
 />


Enter fullscreen mode Exit fullscreen mode

now choose an image file from your computer directory and see how it goes.

check the console once the image is uploaded. option + command + j is the hot key for devtools in chrome.

you should see something like this

Alt Text

now we need to make an onSubmit function for the form that does some complex things

  1. uses helper functions from an external API.

  2. uses a lot of asynchronous code.

  3. gets a response from firebase and sets an imageUrl as a string to an object in state.

make the skeleton of the function

const handleFireBaseUpload = e => {
  e.preventDefault()
console.log('start of upload')
// async magic goes here...

}
Enter fullscreen mode Exit fullscreen mode

I'm going to add console.logs so you can see every step of the way and diagnose problems as they happen.

lets add a button to the form and the onSubmit to the top of the form tag.

When you press the button it will console log start of upload.

the form will look like this.

  <form onSubmit={handleFireBaseUpload}>
        <input 
          type="file"
          onChange={handleImageAsFile}
        />
        <button>upload to firebase</button>
      </form>

Enter fullscreen mode Exit fullscreen mode

lets start with some error handling

 // async magic goes here...
    if(imageAsFile === '' ) {
      console.error(`not an image, the image file is a ${typeof(imageAsFile)}`)
    }

Enter fullscreen mode Exit fullscreen mode

the error message will tell you if you didn't upload an image or it was null or undefined.

make sure that you are in the part of your console that will display errors since we are using console.error and not console.log

below that now we can start the uploading process.

we are creating an uploadTask variable add this right below the if statment

const uploadTask = storage.ref(`/images/${imageAsFile.name}`).put(imageAsFile)
Enter fullscreen mode Exit fullscreen mode

now if you check your firebase console you will see that the image is there.

nice

now, below the const uploadTask, grab the image from firebase as an imageUrl.

with the uploadTask.on( //internet magic inside ) method

this will run through a snapshot of what is happening which we will console.log

we will add an error handler after the snapshot is taken.

use an anonymous function to do the rest...

grab a storage reference as a child.

get the download URL from the file path on the firebase side.

then set the imageAsUrl key with what firebase gives us as the value.

this function will look like this.

//initiates the firebase side uploading 
    uploadTask.on('state_changed', 
    (snapShot) => {
      //takes a snap shot of the process as it is happening
      console.log(snapShot)
    }, (err) => {
      //catches the errors
      console.log(err)
    }, () => {
      // gets the functions from storage refences the image storage in firebase by the children
      // gets the download url then sets the image from firebase as the value for the imgUrl key:
      storage.ref('images').child(imageAsFile.name).getDownloadURL()
       .then(fireBaseUrl => {
         setImageAsUrl(prevObject => ({...prevObject, imgUrl: fireBaseUrl}))
       })
    })
Enter fullscreen mode Exit fullscreen mode

that was a huge sequence of events so let me give you the whole function.

const handleFireBaseUpload = e => {
      e.preventDefault()
    console.log('start of upload')
    // async magic goes here...
    if(imageAsFile === '') {
      console.error(`not an image, the image file is a ${typeof(imageAsFile)}`)
    }
    const uploadTask = storage.ref(`/images/${imageAsFile.name}`).put(imageAsFile)
    //initiates the firebase side uploading 
    uploadTask.on('state_changed', 
    (snapShot) => {
      //takes a snap shot of the process as it is happening
      console.log(snapShot)
    }, (err) => {
      //catches the errors
      console.log(err)
    }, () => {
      // gets the functions from storage refences the image storage in firebase by the children
      // gets the download url then sets the image from firebase as the value for the imgUrl key:
      storage.ref('images').child(imageAsFile.name).getDownloadURL()
       .then(fireBaseUrl => {
         setImageAsUrl(prevObject => ({...prevObject, imgUrl: fireBaseUrl}))
       })
    })
    }

Enter fullscreen mode Exit fullscreen mode

now if you console.log imgAsUrl you will see this

Alt Text
look at the very last console output

now lets display our image to the screen.

below the form enter this code.


//end of form 

<img src={imageAsUrl.imgUrl} alt="image tag" />

//closing tag for div

Enter fullscreen mode Exit fullscreen mode

there you have it you can now upload images. if you want to give them there images titles you can add keys: to the initState object.

you can make another form and reference the image from firebase

Credits:

I relied on this youtube video but it was in classes and I used hooks.

conclusion:

image uploads are like everything else in programming. with time and practice, they get easier.

firebase is a wonderful developer tool and I encourage you to check out some of there other features.

once again if someone knows a better way of doing this please list this in the comments below. if you have any questions please don't be afraid to leave a comment ether.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player