Track upload progress and download files

Photo by Maarten van den Heuvel on Unsplash

Overview

In the previous parts, you learned how to set up a Firebase Storage service and wrote custom rules for your storage bucket. You also learned how to use Image Picker to upload photos locally and use the storage API to save photos to your bucket.

In this last part of the series, I’ll show you the following.

  • How to monitor the upload progress of your photo
  • Render a progress bar while waiting to fetch the photo from storage
  • Get the download URL to display the photo from storage

1. Monitor upload progress

In the previous part, UploafFile/index.js had the following logic.

Code in UploadFile/index.js so far

Make the following changes to the uploadFile function.

You no longer need to resolve the promise nor set the local state for the image URI — as these two steps will be outsourced to a function called monitorFileUpload that you’re going to write shortly.

You’re now saving the results of uploadFileToFirebase in a variable called uploadTaks and passing it as a parameter to monitorFileUpload.

Add the following code just on top of the uploadFile function.

const monitorFileUpload = uploadTask => {
uploadTask.on('state_changed', snapshot => {
switch (snapshot.state) {
case 'running':
setImageURI(null);
break;
case 'success':
snapshot.ref.getDownloadURL().then(downloadURL => {
setImageURI({ uri: downloadURL });
});
break;
default:
break;
}
});
};
const uploadFile = () => // ..

The above function takes uploadTask as the argument and uses an observer method on('state_changed', callback) to track state changes. The observer takes two arguments. The first argument is a string parameter, state_changed, and the second argument is a callback with a snapshot parameter.

You can find more info on tracking upload progress in the official docs here.

With a switch statement, we check snapshot.state for its different cases (i.e., running, success) and update our logic accordingly.

In case snapshot.state returns a success message, we use snapshot.ref.getDownloadURL() to get the remote URL of the uploaded file. We then set the local state to that URL.

Time to test the app. Refresh you simulator, and add a new post. After waiting for a while (until the photo is uploaded and the remote URL is created), you should see the photo displayed on the screen.

2. Build the Progress Bar

As a best practice, you want to show users a progress bar while waiting for the photo to be fetched from storage. To do this, I’ll show you how to leverage the task.on() observer function to build a progress bar for your app.

Start by adding the following function in utils/index.js.

export const uploadProgress = ratio => Math.round(ratio * 100);

The above function takes ratio and returns a rounded percentage. Add it to the imports in UploadFile/index.js.

import {
imagePickerOptions,
uploadFileToFireBase,
uploadProgress,
} from '../../utils';

At this point, you need two things.

  • Set the initial value of progress in the local state.
  • Hide the progress bar when the photo is ready for display.

Add the following code for local state (marked in bold).

const [upload, setUpload] = useState({
loading: false,
progress: 0,
});
const [imageURI, setImageURI] = useState(null);

Add the following code in monitorFileUpload(marked in bold).

const monitorFileUpload = task => {
task.on('state_changed', snapshot => {
const progress = uploadProgress(
snapshot.bytesTransferred / snapshot.totalBytes
);

switch (snapshot.state) {
case 'running':
setImageURI(null);
setUpload({ loading: true, progress });
break;
case 'success':
snapshot.ref.getDownloadURL().then(downloadURL => {
setImageURI({ uri: downloadURL });
setUpload({ loading: false });
});
break;
default:
break;
}
});
};

As you see above, we can access the bytesTransferred and totalBytes through the snapshot parameter.

We pass the ratio snapshot.bytesTransferred/snapshot.totalBytes to the uploadProgress defined in utils/index.js to get the percentage of the upload progress.

In case the upload is still running, we set loading to true and we assign progress to the state. When the upload is successful, we set loading to false.

Add the following code (marked in bold) inside the return() statement.

return (
<Container>
<StatusBar barStyle="dark-content" />
<Button title="New Post" onPress={uploadFile} color="green" />
{imageURI && <Picture source={imageURI} />}
{upload.loading && (
<>
<Skeleton />
<ProgressBar bar={upload.progress} />
</>
)}
</Container>
);

Whenever upload.loadingis true, we display a Skeleton component with a ProgressBar()(to be defined shortly). Notice that ProgressBar takes the props bar={upload.progress} to be used to set the width of the bar.

You need to add the Skeleton and ProgressBar styled-components. Add the following (marked in bold) into styles/index.js.

import styled from 'styled-components/native';
export const Container = styled.SafeAreaView`
flex: 1;
justify-content: center;
`;
export const Picture = styled.Image.attrs({
resizeMode: 'contain',
})`
height: 300;
width: 100%;
`;
export const ProgressBar = styled.View`
background-color: #039ae5;
height: 3;
width: ${props => props.bar}%;
align-items: flex-start;
`;
export const Skeleton = styled.View`
height: 300;
width: 100%;
background-color: #ebebeb;
`;

Notice the dynamic width of ProgressBar takes the bar props you defined earlier.

Import these two new components in UploadFile/index.js.

import { Container, Picture, Skeleton, ProgressBar } from '../../styles';

The final code in UploadFile/index.js should look like this.

The entire logic in UploadFile/index.js

Time to test your app. Launch or refresh your simulator, and add a new photo.

Image Picker with Firebase Storage and progress bar to track upload

As you can see, a skeleton placeholder with a blue progress bar is shown while the photo is uploaded to storage.

Conclusion

Congratulations on completing this series of tutorials. You learned how to use Image Picker to upload photos to Firebase Storage and track the upload progress. Then you displayed the photo on the screen from the remote URL.

I hope you enjoyed it. Take care, and see you in the next one.