How to upload images on firebase storage using react native expo - javascript

I'm trying to upload images on firebase storage using my expo app. I went through their documentation and tried to implement using the docs. but whenever I upload I don't get any error but the image that appears on firebase is just a plain white box
Images uploaded to Firebase
Error when viewing the preview
This is my whole code
import React, { useState } from 'react';
import { Image, View, Text, Button, ActivityIndicator } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import { app } from './firebaseConfig';
import { getStorage, ref, uploadBytesResumable, getDownloadURL } from "firebase/storage";
import 'firebase/storage';
const App = () => {
const [image, setImage] = useState(null);
const [uploading, setUploading] = useState(false);
const pickImage = async () => {
try {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
if (!result.canceled) {
console.log(result.assets[0].uri)
setImage(result.assets[0].uri);
}
} catch (E) {
console.log(E);
}
};
const storage = getStorage(app);
// Create the file metadata
/** #type {any} */
const metadata = {
contentType: 'image/jpeg'
};
// Upload file and metadata to the object 'images/mountains.jpg'
const storageRef = ref(storage, 'images/' + Date.now());
const uploadTask = uploadBytesResumable(storageRef, image, metadata);
console.log(storageRef)
// 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;
console.log('Upload is ' + progress + '% done');
switch (snapshot.state) {
case 'paused':
console.log('Upload is paused');
break;
case 'running':
console.log('Upload is running');
break;
}
},
(error) => {
// A full list of error codes is available at
// https://firebase.google.com/docs/storage/web/handle-errors
switch (error.code) {
case 'storage/unauthorized':
// User doesn't have permission to access the object
break;
case 'storage/canceled':
// User canceled the upload
break;
// ...
case 'storage/unknown':
// Unknown error occurred, inspect error.serverResponse
break;
}
},
() => {
// Upload completed successfully, now we can get the download URL
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
console.log('File available at', downloadURL);
});
}
);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button title="Pick an image from camera roll" onPress={pickImage} />
{image && (
<>
<Image source={{ uri: image }} style={{ width: 200, height: 200 }} />
</>
)}
{uploading && <ActivityIndicator />}
</View>
);
};
export default App;

The URL path result.assets[0].uri returned by expo-image-picker is a reference to image data but not contains actual image data.
As firebase expects to upload binary data, the app requires to fetch image binary data first.
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;
};
const imageBlob = await getBlobFroUri(image)
const uploadTask = uploadBytesResumable(storageRef, imageblob, metadata);
I wrote an in-depth article on this subject - https://dev.to/emmbyiringiro/upload-image-with-expo-and-firebase-cloud-storage-3481

Related

How to make firebase image path work as an image on different devices on React Native?

I have a function that requests the user for an image from their phone. Once this is given, the path is uploaded to firebase and looks something like this:
file:///var/mobile/Containers/Data/Application/9C627709-0F0C-4A2A-939E-9206DA91032C/Library/Caches/ExponentExperienceData/%2540user%252FAPP/ImagePicker/1B5D0907-8ED5-46DC-B216-7DEF7992C1BF.jpg"
The problem is that on the attempt to display it only displays on the phone that this image has been uploaded on, since other devices do not possess this image.
How can I convert the firebase path/image into an image that is displayable in all phones, even though this image does not exist on all users devices?
Here is some important bits of code:
function that gets the library image
const addImage = async () => {
let image = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
if (!image.cancelled) {
setGalleryImage(image.uri);
}
};
function that adds the image to firebase:
const createPhoto = async () => {
await db
.collection("data")
.doc(route?.params?.docId)
.set(
{
galleryImage,
}
)
.then(() => {
})
.catch(error => alert(error));
};
Jsx that displays the image:
<Image
source={{ uri: galleryImage }}
onLoadStart={() => setIsLoading(true)}
onLoadEnd={() => setIsLoading(false)}
/>
Any help is appreciated
This is because, you are saving the local path of the image, hence it can not be displayed on other phones. You need to upload the image to a storage bucket and save the URL of that image on the db.
Convert image to blob
const blob = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function() {
resolve(xhr.response);
};
xhr.onerror = function() {
reject(new TypeError("Network request failed"));
};
xhr.responseType = "blob";
// image.uri is the local path of the image on the device
xhr.open("GET", image.uri, true);
xhr.send(null);
})
Upload image to storage
const storageRef = ref(storage, `profile-pictures/${userContext.user.uid}/${name}`);
const task = uploadBytesResumable(storageRef, blob)
task.on('state_changed', (snapshot) => {
const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100
}, (err) => {
reject(err)
}, () => {
getDownloadURL(task.snapshot.ref)
.then((downloadURL) => {
// image successfully uploaded, save "downloadURL" in your db
})
})

Cannot get data out of composable to fetch firebase data

I have a composable in vue that uploads data to firebase storage. It works, but I cannot get the data out of the composable. I think it has something to do with a return or where I'm defining terms?
The code below is built around the documentation available for firebase Storage (https://firebase.google.com/docs/storage/web/upload-files).
useStorage.js
import { ref } from "vue";
import { projectStorage } from "../firebase/config";
import {
uploadBytesResumable,
getDownloadURL,
ref as storageRef,
} from "#firebase/storage";
const useStorage = () => {
const error = ref(null);
const url = ref(null);
const filePath = ref(null);
const uploadImage = async (file) => {
filePath.value = `images/${file.name}`;
const storageReference = storageRef(projectStorage,
filePath.value);
const uploadTask =
uploadBytesResumable(storageReference, file);
await uploadTask.on(
"state_changed",
(snapshot) => {
const progress =
(snapshot.bytesTransferred / snapshot.totalBytes)
* 100;
console.log("Upload is " + progress + "% done");
},
(err) => {
console.log(err);
error.value = err.message;
},
() => {
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL)
=> {
console.log("File available at", downloadURL);
url.value = downloadURL;
console.log(url.value); <--HAS CORRECT VALUE
return url.value; <--DOESNT DO ANYTHING
});
}
);
console.log(url.value);
};
return { url, filePath, error, uploadImage }; <--URL IS
NOT RETURNING OUT OF THIS COMPOSABLE
};
export default useStorage;
A simpeler approach would be to await getDownloadUrl: url.value = await getDownloadURL(uploadTask.snapshot.ref). Then, you can get rid of the .then on that function. Right now, return url.value is assigned to nothing.
Warning: Also write some sort of catch - in case something would go wrong - in a production environment.

Not able to upload image on Firebase Storage

I am trying to upload Image using Firebase in Firebase Storage, but file doesn't uploads completely. It shows the size of image 9 bytes only, and when downloaded, it can't be previewed.
Here is the code i am using:-
const [image, setImage] = useState(null)
const [uploading, setUploading] = useState(false);
const [transferred, setTransferred] = useState(0);
const uploadImage = async () => {
if( image == null ) {
return null;
}
const uploadUri = image;
let filename = uploadUri.substring(uploadUri.lastIndexOf('/') + 1);
console.log(filename)
// Add timestamp to File Name
const extension = filename.split('.').pop();
const name = filename.split('.').slice(0, -1).join('.');
filename = name + Date.now() + '.' + extension;
console.log("filename")
console.log(filename)
setTransferred(0);
const storageRef = firebase.storage().ref(`photos/${filename}`);
console.log("storageRef")
const task = storageRef.put(uploadUri);
console.log("storageRef")
console.log(storageRef)
// Set transferred state
task.on('state_changed', (taskSnapshot) => {
console.log(
`${taskSnapshot.bytesTransferred} transferred out of ${taskSnapshot.totalBytes}`,
);
setTransferred(
Math.round(taskSnapshot.bytesTransferred / taskSnapshot.totalBytes) *
100,
);
console.log(transferred)
});
try {
await task;
const url = await storageRef.getDownloadURL();
setUploading(false);
setImage(null);
alert(
'Image uploaded!',
'Your image has been uploaded to the Firebase Cloud Storage Successfully!',
);
return url;
} catch (e) {
console.log(e);
return null;
}
};
const takephotofrommlib = () => {
ImagePicker.openPicker({
width: 300,
height: 300,
cropping: true,
}).then((image) => {
console.log(image);
const imageUri = Platform.OS === 'ios' ? image.path : image.path;
setImage(image.path);
console.log("image.path")
console.log(image.path)
});
};
I am using react-native-image-crop-picker. I am using Firebase but not react-native firebase. Please Help!
i just make a file uploadFile.js
here's a code
import storage from "#react-native-firebase/storage";
export default async function uploadFile(ref,fileName,file) {
if(!file) return
const tarea=file
if (tarea&& tarea.indexOf("http://") == 0 || tarea&&tarea.indexOf("https://") == 0)
{
// do something here
return tarea
}
const reference = storage().ref(`${ref}/${fileName}`);
await reference.putFile(file).catch(err=>{console.log("error upload",err);
})
return await storage()
.ref(`${ref}/${fileName}`)
.getDownloadURL().catch(err=>{console.log("download eror",err);
});
}
you can use like this
img= await uploadFile('photos',"fileName",filePath)
In img you can get the download url

Try to upload an image into firebase storage

i'm trying to upload and image from a device to firebase storage but i don't know which format i should use. i've try with put and putString, but both of them gave me invalid argument.
This is the code to pick and upload the image.
const pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
if (!result.cancelled) {
setImage(result.uri);
}
};
const uploadImage = async () => {
if (!image) {
Alert.alert(
'You have to choose an image first'
);
} else {
const uri = image;
console.log(uri);
const filename = uri.substring(uri.lastIndexOf('/') + 1);
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri;
setUploading(true);
setTransferred(0);
const task = firebase.storage()
.ref(filename)
.put(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!'
);
setImage(null);
}
};
This is the uri of the image (the console.log output) :
This is the error using .put(uploadUri):
This is the error using .putString(uploadUri, 'data_url') :
In order to upload an image on Firebase storage using put you need to pass a blob as param instead of string.
For example you can do something like this:
import path from 'path'
const uid = 'image-id'
const fileName = uid + path.extname(uri)
const response = await fetch(uri)
const blob = await response.blob()
const uploadImage = firebase
.storage()
.ref()
.put(blob, {
contentType: `image/${path.extname(uri).split('.').pop()}`
})
uploadImage.on(
'state_changed',
snapshot => {
// progress
},
err => {
// error
},
() => {
// complete
}

Issues while uploading an image to firebase storage with Antd upload action

I'm using antd picture-wall/card example to upload images to my firebase storage with this reference code and the only place I'm changing is action property on <Upload> component.
On the action property, I'm using a function that uploads the images to firebase storage instead of a link both are accepted as seen in docs.
My action function looks like this;
export async function uploadImage(file) {
const storage = firebase.storage()
const metadata = {
contentType: 'image/jpeg'
}
const storageRef = await storage.ref()
const imageName = generateHashName() //a unique name for the image
const imgFile = storageRef.child(`Vince Wear/${imageName}.png`)
return imgFile.put(file, metadata)
}
Issue comes, The image uploads to firebase successfully, but I keep getting antd response handling errors and possibly not sure what action function should return, even though, is written in the docs that it should return a promise.
Error message:
XML Parsing Error: syntax error
Location: http://localhost:3000/[object%20Object]
Line Number 1, Column 1:
Errors also appear as a red border on the uploaded image thumbnail.
Requested help, What should my action function return to get rid of errors. I can parse my firebase response and return the necessary details to antd upload action.
Using
"antd": "^3.9.2",
"firebase": "^5.8.5",
"react": "^16.7.0",
You can use customRequest prop to fix this issue. Have a look
class CustomUpload extends Component {
state = { loading: false, imageUrl: '' };
handleChange = (info) => {
if (info.file.status === 'uploading') {
this.setState({ loading: true });
return;
}
if (info.file.status === 'done') {
getBase64(info.file.originFileObj, imageUrl => this.setState({
imageUrl,
loading: false
}));
}
};
beforeUpload = (file) => {
const isImage = file.type.indexOf('image/') === 0;
if (!isImage) {
AntMessage.error('You can only upload image file!');
}
// You can remove this validation if you want
const isLt5M = file.size / 1024 / 1024 < 5;
if (!isLt5M) {
AntMessage.error('Image must smaller than 5MB!');
}
return isImage && isLt5M;
};
customUpload = ({ onError, onSuccess, file }) => {
const storage = firebase.storage();
const metadata = {
contentType: 'image/jpeg'
}
const storageRef = await storage.ref();
const imageName = generateHashName(); //a unique name for the image
const imgFile = storageRef.child(`Vince Wear/${imageName}.png`);
try {
const image = await imgFile.put(file, metadata);
onSuccess(null, image);
} catch(e) {
onError(e);
}
};
render () {
const { loading, imageUrl } = this.state;
const uploadButton = (
<div>
<Icon type={loading ? 'loading' : 'plus'} />
<div className="ant-upload-text">Upload</div>
</div>
);
return (
<div>
<Upload
name="avatar"
listType="picture-card"
className="avatar-uploader"
beforeUpload={this.beforeUpload}
onChange={this.handleChange}
customRequest={this.customUpload}
>
{imageUrl ? <img src={imageUrl} alt="avatar" /> : uploadButton}
</Upload>
</div>
);
}
}
Just leaving this here incase anyone wanted to track the progress of the file aswell
const customUpload = async ({ onError, onSuccess, file, onProgress }) => {
let fileId = uuidv4()
const fileRef = stg.ref('demo').child(fileId)
try {
const image = fileRef.put(file, { customMetadata: { uploadedBy: myName, fileName: file.name } })
image.on(
'state_changed',
(snap) => onProgress({ percent: (snap.bytesTransferred / snap.totalBytes) * 100 }),
(err) => onError(err),
() => onSuccess(null, image.metadata_)
)
} catch (e) {
onError(e)
}
}

Categories