Uploading Cropped Image to Firebase Storage

Home|Source Codes|Uploading Cropped Image to Firebase Storage

Published by

PinoyFreeCoder

Date :

Sun Apr 07 2024
TypeScript
React-Cropper
Free

Welcome to our comprehensive tutorial on React Image Cropper Firebase Upload! In this in-depth guide, we'll walk you through the process of integrating a powerful image cropper into your React applications and seamlessly uploading cropped images to Firebase storage.

Full Tutorial

To learn more about the application watch the video below.

App.tsx code snippet

    
      import Cropper from "react-cropper";
      import "cropperjs/dist/cropper.css";
      import { useState } from "react";
      import { getDownloadURL, ref, uploadBytesResumable } from "firebase/storage";
      import { projectStorage } from "./firebase/config";
  
      function App() {
        const [dataUrl, setDataUrl] = useState<string>(""); //cropped image
        const [cropper, setCropper] = useState<any>();
        const [image, setImage] = useState(""); // default image preview
  
        const changeHandler = (e: any) => {
          e.preventDefault();
          let files;
          if (e.dataTransfer) {
            files = e.dataTransfer.files;
          } else if (e.target) {
            files = e.target.files;
          }
          const reader = new FileReader();
          reader.onload = () => {
            setImage(reader.result as any);
          };
          reader.readAsDataURL(files[0]);
        };
  
        const getCrop = () => {
          if (cropper !== undefined) {
            setDataUrl(cropper.getCroppedCanvas().toDataURL());
          }
        };
  
        const UploadImage = async () => {
          const fileName = _cropped_png;
          const arr = dataUrl.split(",");
          if (arr.length < 2) {
            throw new Error("Invalid data url");
          }
  
          const mime = arr[0].match(/:(.*?);/);
          if (!mime) {
            throw new Error("Invalid data URL format");
          }
          const mimeType = mime[1];
          const bstr = atob(arr[1]);
          let n = bstr.length;
          const u8arr = new Uint8Array(n);
          while (n--) {
            u8arr[n] = bstr.charCodeAt(n);
          }
          const FinalFile = new File([u8arr], fileName, { type: mimeType });
          //upload to firebase storage
          await firebaseUpload(FinalFile);
        };
  
        const firebaseUpload = async (image: File): Promise<string | undefined> => {
          return new Promise<string | undefined>((resolve, reject) => {
            const storageRef = ref(projectStorage, "/croppedImages/image.name"); // directory where the image will be saved;
            const metaData = {
              contentType: image.type,
              contentDisposition: "inline",
            };
  
            const uploadTask = uploadBytesResumable(storageRef, image, metaData);
  
            uploadTask.on(
              "state_changed",
              (snapshot) => {
                const percent = Math.round(
                  (snapshot.bytesTransferred / snapshot.totalBytes) * 100
                );
                console.debug(percent); //can be used on percent upload indicators
              },
              (err) => {
                console.error(err);
                reject(err);
              },
              async () => {
                try {
                  const url = await getDownloadURL(uploadTask.snapshot.ref);
                  console.log(url);
                  if (url) {
                    resolve(url); // url can be saved into a database
                  }
                } catch (error) {
                  console.error(error);
                  reject(error);
                }
              }
            );
          });
        };
  
        return (
          <div style="display: flex; gap: 4px">
            <div>
              <Input type="file" onChange={changeHandler} />
              <Cropper
                src={image}
                initialAspectRatio={16 / 9}
                guides={false}
                responsive={true}
                onInitialized={(instance) => {
                  setCropper(instance);
                }}
              />
              <button type="button" onClick={getCrop}>
                Crop Image
              </button>
            </div>
            <div style="display: flex; flex-direction: column; gap: 4px">
              <h2>Preview</h2>
              <img width={"300px"} src={dataUrl} />
              <button type="button" onClick={UploadImage}>
                Upload Image
              </button>
            </div>
          </div>
        );
      }
  
      export default App;