Upload Image with Expo and Firebase Cloud Storage
Tags: reactnative, expo, firebase
Published: 2022-02-11 15:01:51

---
title: Upload Image with Expo and Firebase Cloud Storage
published: true
description: Upload Image with Expo and Firebase Cloud Storage.The solution is applied also with React Native bare workflow
tags: ReactNative,Expo,Firebase
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/chwxwt61u24ohme9l0gf.png

---

User-uploaded images are common in mobile apps, how can be done with Expo and Firebase cloud storage?  - let's find out the solution.

### Request a device media library files  access permission

Both iOS and Android require a user to grant access to the app before reading or writing to the media library. 

Throughout this article, we will use `expo-image-picker` to request permission and upload an image from the media library. 

```js

// hasMediaLibraryPermissionGranted.js

import * as ImagePicker from "expo-image-picker";

const hasMediaLibraryPermissionGranted = async () => {
  let granted =  false;

  const permission = await ImagePicker.requestMediaLibraryPermissionsAsync();

  if (!permission.canAskAgain || permission.status === "denied") {
    granted = false;
   
  }

  if (permission.granted) {
    granted = true;
  }

  return granted;
};

export default hasMediaLibraryPermissionGranted

```

### Upload image  from device media library

`expo-image-picker`  exposes `ImagePicker.launchImageLibraryAsync` method to select image or video and copy to app cache then return file **Universal Resource Identifier (URI**) path.

```js
// uploadImageFromDevice.js

const uploadImageFromDevice = async () => {
  let imgURI = null;
  const storagePermissionGranted = await hasMediaLibraryPermissionGranted();

  // Discard execution when  media library permission denied
  if (!storagePermissionGranted) return imgURI;

  let result = await ImagePicker.launchImageLibraryAsync({
    mediaTypes: ImagePicker.MediaTypeOptions.Images,
    allowsEditing: true,
    aspect: [4, 4],
    quality: 1,
  });

  if (!result.cancelled) {
    imgURI = result.uri;
  }

  return imgURI;
};

export default uploadImageFromDevice;


```

### Fetch uploadable image binary data.

The URI return by `ImagePicker.launchImageLibraryAsync`  is a reference to the cached image and doesn't hold the image data.

Most cloud storage providers including Firebase Cloud Storage support a  binary large object (BLOB) for uploading various files types.

Let us fetch  BLOB data from an image URI .

```js

// getBlobFroUri.js

const getBlobFroUri = async (uri) => {
  const blob = await new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.onload = function () {
      resolve(xhr.response);
    };
    xhr.onerror = function (e) {
      reject(new TypeError("Network request failed"));
    };
    xhr.responseType = "blob";
    xhr.open("GET", uri, true);
    xhr.send(null);
  });

  return blob;
};



export default getBlobFroUri

```

### Upload  Binary  data to Firebase Cloud Storage

Image BLOB data are ready now, Firebase provides a cloud object storage service with the free-tier plan for an early-stage app.

```js

// manageFileUpload.js


import firebase from "firebase";

const manageFileUpload = async (
  fileBlob,
  { onStart, onProgress, onComplete, onFail }
) => {
  const imgName = "img-" + new Date().getTime();

  const storageRef = firebase.storage().ref(`images/${imgName}.jpg`);

  console.log("uploading file", imgName);

  // Create file metadata including the content type
  const metadata = {
    contentType: "image/jpeg",
  };

  // Trigger file upload start event
  onStart && onStart();
  const uploadTask = storageRef.put(fileBlob, metadata);
  // Listen for state changes, errors, and completion of the upload.
  uploadTask.on(
    "state_changed",
    (snapshot) => {
      // Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
      const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;

      // Monitor uploading progress
      onProgress && onProgress(Math.fround(progress).toFixed(2));
    },
    (error) => {
      // Something went wrong - dispatch onFail event with error  response
      onFail && onFail(error);
    },
    () => {
      // Upload completed successfully, now we can get the download URL

      uploadTask.snapshot.ref.getDownloadURL().then((downloadURL) => {
        // dispatch on complete event
        onComplete && onComplete(downloadURL);

        console.log("File available at", downloadURL);
      });
    }
  );
};

export default manageFileUpload;

```

Finally, Connect all pieces together

```js
//App.js

import * as React from "react";
import {
  Text,
  View,
  StyleSheet,
  Image,
  Platform,
  useWindowDimensions,
} from "react-native";
import Constants from "expo-constants";
import firebase from "./config/firebase";
import { AntDesign, Feather } from "@expo/vector-icons";
import uplodImageFromDevice from "./uploadImageFromDevice";
import getBlobFromUri from "./getBlobFromUri";
import manageFileUpload from "./manageFileUpload";
export default function App() {
  const [imgURI, setImageURI] = React.useState(null);

  const [isUploading, setIsUploading] = React.useState(false);
  const [progress, setProgress] = React.useState(0);
  const [remoteURL, setRemoteURL] = React.useState("");

  const [error, setError] = React.useState(null);
  const { width } = useWindowDimensions();

  const handleLocalImageUpload = async () => {
    const fileURI = await uplodImageFromDevice();

    if (fileURI) {
      setImageURI(fileURI);
    }
  };

  const onStart = () => {
    setIsUploading(true);
  };

  const onProgress = (progress) => {
    setProgress(progress);
  };
  const onComplete = (fileUrl) => {
    setRemoteURL(fileUrl);
    setIsUploading(false);
    setImageURI(null);
  };

  const onFail = (error) => {
    setError(error);
    setIsUploading(false);
  };
  const handleCloudImageUpload = async () => {
    if (!imgURI) return;

    let fileToUpload = null;

    const blob = await getBlobFromUri(imgURI);

    await manageFileUpload(blob, { onStart, onProgress, onComplete, onFail });
  };

  return (
    <View style={styles.container}>
      <Text style={styles.heading}>Attach and upload image</Text>
      {Boolean(imgURI) && (
        <View>
          <Image
            source={{ uri: imgURI }}
            resizeMode="contain"
            style={{ width, height: width }}
          />
        </View>
      )}

      {!isUploading && (
        <View style={styles.row}>
          <AntDesign
            name="addfile"
            size={36}
            color={imgURI ? "green" : "black"}
            onPress={handleLocalImageUpload}
          />

          {Boolean(imgURI) && (
            <Feather
              name="upload-cloud"
              size={36}
              color="black"
              onPress={handleCloudImageUpload}
            />
          )}
        </View>
      )}

      {isUploading && (
        <View style={styles.uploadProgressContainer}>
          <Text> Upload {progress} of 100% </Text>
        </View>
      )}

      {Boolean(remoteURL) && (
        <View style={{ paddingVertical: 20 }}>
          <Text>
            Image has been uploaded at
            <Text style={{ color: "blue" }}> {remoteURL} </Text>
          </Text>
        </View>
      )}
    </View>
  );
}

```


*Expo Snack preview* -  https://snack.expo.io/@expofire/upload-image-with-expo-and-firebase-cloud-storage


>Stuck with code and need 1:1 assistance. Ping [me](mailto:emmbyiringiro@gmail.com)