Expo Camera Photo Upload To Firebase Storage is undefined - React Native - javascript

I am using import { Camera } from 'expo-camera'; to take pictures. The taken picture is stored in the devicecache. so far so good.
Now I am trying to upload this taken images to Google Firebase Storage using import { getStorage, ref, uploadBytes } from "firebase/storage";
The return of doing a photo is:
{
"width":5472,
"uri":"file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540g1sm0%252Feprojstant/Camera/3689f575-d849-4e3e-b4ea-1ba40b96cf02.jpg",
"height":7296
}
Now I try to upload this like that:
const storageRef = ref(storage, 'some-child');
const file = photo.uri
uploadBytes(storageRef, file).then((snapshot) => {
console.log('Uploaded a blob or file!');
});
After a little delay, there is a file created in firebase/storage . I can open this file by an texteditor. the file includes the text undefined
The assumption is that the uri that i hand over is not the right solution. However, I'm too new to development and can't find any help for react native. do you have an idea, a link or an example? Do I first have to convert the file to a blob or base64 and if so, how?

Everytime after I post a Question, I do find the solution.
I blob the file like that:
const blob = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function () {
resolve(xhr.response);
};
xhr.onerror = function (e) {
console.log(e);
reject(new TypeError("Network request failed"));
};
xhr.responseType = "blob";
xhr.open("GET", uri, true);
xhr.send(null);
});
and uploaded this result like this:
uploadBytes(storageRef, blob).then((snapshot) => {
console.log('Uploaded a blob or file!');
});
Here is the full function for my solution:
const [hasPermission, setHasPermission] = useState(null);
const [type, setType] = useState(Camera.Constants.Type.back);
const [previewVisible, setPreviewVisible] = useState(false)
const [capturedImage, setCapturedImage] = useState(null)
let camera = Camera
const __takePicture = async () => {
if (!camera) return
const photo = await camera.takePictureAsync()
setPreviewVisible(true)
setCapturedImage(photo)
// Create a root reference
const storage = getStorage();
const uri = photo.uri
const blob = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function () {
resolve(xhr.response);
};
xhr.onerror = function (e) {
console.log(e);
reject(new TypeError("Network request failed"));
};
xhr.responseType = "blob";
xhr.open("GET", uri, true);
xhr.send(null);
});
// TODO: UUID #some-child
const storageRef = ref(storage, 'some-child');
uploadBytes(storageRef, blob).then((snapshot) => {
console.log('Uploaded a blob or file!');
});
}
useEffect(() => {
(async () => {
const { status } = await Camera.requestCameraPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
if (hasPermission === null) {
return <View />;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
return (YOUR VIEW RENDERING HERE)

Related

Event { "isTrusted": false } using Expo and XMLHttpRequest

I'm trying to upload(send) a PDF to server using below code snippet:
const xhr = new XMLHttpRequest();
xhr.open("POST", "/upload");
xhr.onload = (e) => {
const response = JSON.parse(xhr.response);
console.log(response);
};
xhr.onerror = (error) => {
console.log(error);
};
xhr.ontimeout = (e) => {
console.log(e, "upload timeout");
};
const formData = new FormData();
formData.append("fileToUpload", {
uri: fileToUpload.uri,
type: `*/*`,
name: fileToUpload.name,
});
xhr.send(formData);
if (xhr.upload) {
xhr.upload.onprogress = ({ total, loaded }) => {
const uploadProgress = loaded / total;
console.log(uploadProgress);
};
}
In response, I'm receiving following error:
Event {
"isTrusted": false,
}
Any other approach to upload a file (pdf) to server using Expo, would also be appreciated.
Thanks
I figured out, that the actual problem was- In android while selecting a file on Android device, DocumentPicker in Expo doesn't provide the complete path to the file, you have to manage it on your own, As I did below.
import * as DocumentPicker from "expo-document-picker";
...
const [singleFile, setSingleFile] = useState(null);
const onSubmit = async () => {
try {
// upload the file
const formData = new FormData();
formData.append("fileToUpload", singleFile);
axios.defaults.headers.post["Content-Type"] =
"multipart/form-data";
const uploadResp = await axios.post(
"/upload",
formData
);
if (uploadResp.status === 200) {
// file uploaded successfully
}
} catch (error) {
}
};
const selectFile = async () => {
await DocumentPicker.getDocumentAsync({
type: "application/pdf",
copyToCacheDirectory: true,
})
.then((response) => {
if (response.type == "success") {
let { name, size, uri } = response;
// >>>>>>>>>>>>> the bug's solution <<<<<<<<<<<<<<<
if (Platform.OS === "android" && uri[0] === "/") {
uri = `file://${uri}`;
uri = uri.replace(/%/g, "%25");
}
let nameParts = name.split(".");
let fileType = nameParts[nameParts.length - 1];
setSingleFile({
name: name,
size: size,
uri: uri,
type: "application/" + fileType,
});
} else {
setSingleFile(null);
}
})
.catch((err) => {
console.error(err);
});
};
...
<TouchableOpacity activeOpacity={0.6} onPress={() => selectFile()}>
<View >
<Text >
Select File
</Text>
</View>
</TouchableOpacity>
...

Uploading image to firebase using expo react-native

I am working on an app and I am using expo, I want to make sure each user can upload an image to firebase, and later publish this image on the profile page.
Using expo this is how I upload images:
const pickImage = async () => {
let pickerResult = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
console.log(pickerResult);
handleImagePicked(pickerResult);
};
the result in the console is:
Object {
"cancelled": false,
"height": 312,
"type": "image",
"uri": "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252Fallergyn-app-77bfd368-65fd-43f9-8c34-9c35cef42c25/ImagePicker/daaa229c-c352-4994-ae18-ca2dbb3534ce.jpg",
"width": 416,
}
and this is how I upload to the firebase:
const handleImagePicked = async (pickerResult) => {
try {
if (!pickerResult.cancelled) {
setImage(pickerResult.uri);
await uploadImageAsync(pickerResult.uri);
console.log("done");
}
} catch (e) {
console.log(e);
alert("Upload failed, sorry :(");
} finally {
}
};
async function uploadImageAsync(uri) {
const blob = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function () {
resolve(xhr.response);
};
xhr.onerror = function (e) {
console.log(e);
reject(new TypeError("Network request failed"));
};
xhr.responseType = "blob";
xhr.open("GET", uri, true);
xhr.send(null);
});
const ref = firebase
.storage()
.ref()
.child("images" + Math.random());
const snapshot = await ref.put(blob);
// We're done with the blob, close and release it
blob.close();
return await snapshot.ref.getDownloadURL();
}
this code works it saves the path of the image this: "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252Fallergyn-app-77bfd368-65fd-43f9-8c34-9c35cef42c25/ImagePicker/daaa229c-c352-4994-ae18-ca2dbb3534ce.jpg" in the firebase under user collection using the uid of the user.
I am not sure if this is good, because I want to make sure the image itself is uploaded to firebase, I saw some threads in StackOverflow regarding this issue either too old or no answers, so I am hoping to get some sort of solution to what I need to do.
if I use
const ref = firebase
.storage()
.ref()
.child("images" + Math.random());
.putFile(uri);
this tells me that putFile is not a function. the same with put(uri)
Try this one. This function returns the path of the saved image from firebase which you will store in the user's document instead.
const handleImagePicked = async (pickerResult) => {
if (!pickerResult.cancelled) {
setImage(pickerResult.uri);
const result = await uploadImageAsync(pickerResult.uri);
if(result) {
console.log('success');
//save the result path to firestore user document
return;
}
alert("Upload failed, sorry :(");
}
};
export const uploadImageAsync = async (uri: string) => {
let filename = uri;
if (Platform.OS === 'ios') {
filename = uri.replace('file:', '');
}
const ext = filename.split('.').pop();
const path = `images/${id}.${ext}`;
const ref = firebase.storage().ref(path);
try {
const response = await fetch(filename);
const blob = await response.blob();
await ref.put(blob);
return path;
} catch {
return null;
}
};
This worked for me , using rn-fetch-blob
import launchImageLibrary from 'react-native-image-picker';
import RNFetchBlob from 'rn-fetch-blob';
import storage from '#react-native-firebase/storage';
const pickImage = () => {
let options = {
mediaType: 'photo',
quality: 0.5,
};
launchImageLibrary(options, (response) => {
console.log('Response = ', response);
uploadImagePicked(response);
});
};
const uploadImagePicked = (response) => {
if (response.fileName) {
const fileName = response.fileName;
var storageRef = storage().ref(`receiptImages/${fileName}`);
RNFetchBlob.fs.readFile(response.uri , 'base64')
.then(data => {
storageRef.putString(data, 'base64', {contentType:"image/jpg"})
.on(
storage.TaskEvent.STATE_CHANGED,
snapshot => {
console.log("snapshot: " + snapshot.state);
console.log("progress: " + (snapshot.bytesTransferred / snapshot.totalBytes) * 100);
if (snapshot.state === storage.TaskState.SUCCESS) {
console.log("Success");
}
},
error => {
console.log("image upload error: " + error.toString());
},
() => {
storageRef.getDownloadURL()
.then((downloadUrl) => {
console.log("File available at: " + downloadUrl);
})
})
})
.catch(error => {
console.log(error);
})
}
else {
console.log("Skipping image upload");
}
}

trigger a function to run before running the function that's called

I have 3 functions. One takes picture, one compresses image, one uploads an image.
Button is pressed to take picture (this works fine)
Another button is pressed to upload picture but first it runs the compress image and then uploads it but I'm hitting error of ReadableNativeMap cannot be cast to java.lang.String which does help me much. Am I not passing the values correctly?
//Take picture
const takePicture = async () => {
if (cameraRef.current) {
const options = { quality: 1, base64: true, skipProcessing: true };
const result = await cameraRef.current.takePictureAsync(options);
setImageResult(result.uri);
}
};
//compression image
const compressimagestuff = async (uncompressedImage) => {
const manipResult = ImageManipulator.manipulateAsync(
uncompressedImage,
{ compress: 0.3, format: ImageManipulator.SaveFormat.JPEG }
);
return manipResult.url;
};
// uploading it to firebase
const uploadImageAsync = async (uncompressedImage) => {
let compressedImageUrl = compressimagestuff(uncompressedImage);
const blob = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function () {
resolve(xhr.response);
};
xhr.onerror = function (e) {
console.log(e);
reject(new TypeError('Network request failed'));
};
xhr.responseType = 'blob';
xhr.open('GET', compressedImageUrl, true);
xhr.send(null);
});
const ref = firebase
.storage()
// the file that it will get saved to
.ref('Front/')
// giving it a unique name
.child(await uuid.v4())
const snapshot = await ref.put(blob);
blob.close();
return await snapshot.ref.getDownloadURL();
};
Button that is called to upload the picture taken
const uploadPreviewButton = () => (
<TouchableOpacity disabled={wasPressed} onPress={() => { uploadImageAsync(imageResult); setWasPressed(true); }} style={[styles.uploadButton, wasPressed ? { backgroundColor: 'red'} : {} ]}>
<Icon ...>
</TouchableOpacity>
);
compressimagestuff is an async function but you don't wait for it. Change this:
const uploadImageAsync = async (uncompressedImage) => {
let compressedImageUrl = compressimagestuff(uncompressedImage);
to:
const uploadImageAsync = async (uncompressedImage) => {
let compressedImageUrl = await compressimagestuff(uncompressedImage);
// ^

Convert File path to File Object in JavaScript

I want to upload file on firebase using JavaScript my file on my server and also have file path but firebase doesn't allow to upload file from file path and it shows the following error message:
code_: "storage/invalid-argument"
message_: "Firebase Storage: Invalid argument in `put` at index 0: Expected Blob or File."
name_: "FirebaseError"
serverResponse_: null
How can I upload the file or convert the file path to file object?
You can use this function to upload file in firebase. the uri is the file path which further creates the file type required in firebase.
const uploadImage = async (uri) => {
const uniqid = () => Math.random().toString(36).substr(2, 9);
const ext = uri.split('.').pop(); // Extract image extension
const filename = `${uniqid()}.${ext}`; // Generate unique name
const ref = fb.storage().ref().child(`images/${filename}`);
const response = await fetch(uri);
// const blob = await response.blob();
const blob = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function () {
resolve(xhr.response);
};
xhr.onerror = function (e) {
console.log(e);
reject(new TypeError('Network request failed'));
};
xhr.responseType = 'blob';
xhr.open('GET', uri, true);
xhr.send(null);
});
const snapshot = await ref.put(blob);
blob.close();
const imgUrl = await snapshot.ref.getDownloadURL();
return imgUrl;
}
after this function you can call this function to upload your Image in firebase database like this,
const formSubmit= async(values, action)=>{
console.log(values);
try{
uploadImage(image) //this is my image upload code
.then( async(res) => {
setLoader(false);
var user = fb.auth().currentUser;
await user.updateProfile({
displayName: values.name,
photoURL: res // use the response url to store in mapped data table
}).then(function(res) {
console.log(res)
}).catch(function(error) {
console.log(error)
});
})
.catch(error => {
console.log('it does not work')
console.error(error)
})
}catch(error){
console.log(error)
}
}

Expo firebase 7.9.0 can't get downloadUrl

My method does manage to load the image from expo to firebase storage but I can't seem to get the download URL.
const uploadImage = async (uri) => {
const uniqid = () => Math.random().toString(36).substr(2, 9);
const ext = uri.split('.').pop(); // Extract image extension
const filename = `${uniqid()}.${ext}`; // Generate unique name
const response = await fetch(uri);
const blob = await response.blob();
var ref = firebase
.storage()
.ref()
.child('images/' + filename);
ref.getDownloadURL().then((url) => console.log(url));
return ref.put(blob);
};
Here is the error I get
FirebaseStorageError {
"code_": "storage/object-not-found",
"message_": "Firebase Storage: Object 'images/gebwu7tnh.jpg' does not exist.",
"name_": "FirebaseError",
"serverResponse_": "{
"error": {
"code": 404,
"message": "Not Found. Could not get object",
"status": "GET_OBJECT"
}
}"
This is what I found that helped me from researching. I did refactor my firebase to use a higher order component. Here is my firebase method.
uploadImageAsync: async (uri) => {
const uniqid = () => Math.random().toString(36).substr(2, 9);
const ext = uri.split('.').pop(); // Extract image extension
const filename = `${uniqid()}.${ext}`; // Generate unique name
const ref = firebase
.storage()
.ref()
.child('images/' + filename);
const blob = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function () {
resolve(xhr.response);
};
xhr.onerror = function (e) {
console.log(e);
reject(new TypeError('Network request failed'));
};
xhr.responseType = 'blob';
xhr.open('GET', uri, true);
xhr.send(null);
});
const snapshot = await ref.put(blob);
blob.close();
const imgUrl = await snapshot.ref.getDownloadURL();
console.log(imgUrl);
return imgUrl;
},
};
Here is how I implemented it in my component
const setImage = async (uri) => {
try {
return await firebase.uploadImageAsync(uri);
} catch (error) {
console.log(error);
}
};

Categories

Resources