'Upload base64 Image to Firebase React native
I try to upload a "base64" image to Firebase by using React Native on iOS. But when I try to upload the image, I get following error:
undefined is not an object (evaluating 'uri.substring')
I get my image by using route.params and if I display the image in a view like this, the image is displayed.
<Image style={styles.image} source={{ uri: `data:image/png;base64,${myImage}` }}/>
Should I do anything else if the image is in "base64" or what else should I do?
This is my code:
// Here is how I get the image
const { myImage } = props.route.params;
const uploadImage = async () => {
const {uri} = myImage;
const filename = uri.substring(uri.lastIndexOf('/') + 1);
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri;
setUploading(true);
setTransferred(0);
const task = storage()
.ref(filename)
.putFile(uploadUri);
// set progress state
task.on('state_changed', snapshot => {
setTransferred(
Math.round(snapshot.bytesTransferred / snapshot.totalBytes) * 10000
);
});
try {
await task;
} catch (e) {
console.error(e);
}
setUploading(false);
Alert.alert(
'Photo uploaded!',
'Your photo has been uploaded to Firebase Cloud Storage!'
);
};
Solution 1:[1]
Looking at your current code, you take myImage out of props.route.params, and this is a string of Base 64 characters corresponding to a PNG image (such as iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAMSURBVBhXY/j//z8ABf4C/qc1gYQAAAAASUVORK5CYII= - the data of a PNG for a single #FFFFFF pixel).
const { myImage } = props.route.params;
Below that you try to get a property uri out of this myImage string. As this property doesn't exist, you will get undefined. This uri = undefined value then throws an error on the next line.
const { uri } = myImage; // uri is undefined! ("".uri === undefined)
const filename = uri.substring(uri.lastIndexOf('/') + 1); // throws error!
The correct way to upload a Data URL would be to use the Reference#putString() method as covered in the documentation here.
Applying this to your code, you would use:
const { myImage } = props.route.params;
const uploadImage = async () => {
const dataUrl = `data:image/png;base64,${myImage}`;
// you could use a Firestore Doc ID, a RTDB Push ID or
// some `uuid` implementation to generate a suitable filename.
const storageRef = storage()
.ref(/* provide a path for the image */);
const uploadTask = storageRef
.putString(dataUrl, 'data_url');
uploadTask.on('state_changed', snapshot => {
setTransferred(
Math.round(snapshot.bytesTransferred / snapshot.totalBytes) * 10000
);
});
try {
await uploadTask;
setUploading(false);
Alert.alert(
'Photo uploaded!',
'Your photo has been uploaded to Firebase Cloud Storage!'
);
} catch (err) {
// TODO: Check value of `err.code` and handle appropriately.
console.error('Upload failed: ', err);
Alert.alert(
'Photo upload failed!',
'Your photo didn\'t upload properly!'
);
}
}
To prevent overwriting someone else's data and make security rules easier to implement, you should prefix the uploaded file with the user's ID similar to:
const storageRef = storage()
.ref('userUploads')
.child(firebase.auth().currentUser.uid)
.child(/* generated image ID */);
// builds a reference to /userUploads/someUserId/someImageId
Solution 2:[2]
If your image data string looks something like this:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABQAAAALQCAYAAADPfd1WAAAAAXNSR0IArs4c6QAAIABJREFUeF7svXmPLEly4GcRkVnnO/r1656+5uRcJEGAnMEO ...
Then you can use the putString method - but you need to remove the starting info data:image/png;base64, first.
await storage.ref("pictures/thumbnail.png").putString(thumbnailImageData.split("data:image/png;base64,")[1], "base64");
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | exile97 |
