I have a react native project and I need to upload multiple images to firebase storage. in my code below I can select images from the device, and when I upload to the storage, only the first image gets uploaded. I need a way to be able to upload array of images to firebase storage.
I need to upload these images to storage because I will store the array of images in firestore.
const pickImages = async () => {
setIsLoading(true);
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
// allowsEditing: true,
allowsMultipleSelection: true,
selectionLimit: 10,
aspect: [4, 3],
quality: 1,
});
if (!result.canceled) {
setImages(result.uri);
}};
useEffect(() => {
const uploadImage = async () => {
const blobImage = 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";
xhr.open("GET", images, true);
xhr.send(null);
});
const metadata = {
contentType: "image/jpeg",
};
const storageRef = ref(storage, "Service/" + blobImage.data.name);
const uploadTask = uploadBytesResumable(storageRef, blobImage, metadata);
uploadTask.on(
"state_changed",
(snapshot) => {
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) => {
switch (error.code) {
case "storage/unauthorized":
break;
case "storage/canceled":
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);
});
}
);
};
if (images != null) {
uploadImage();
setImages(null);
}}, [images]);
Related
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)
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>
...
I am trying to write a function that uploads a jpeg image to firebase storage and returns the download link of the uploaded file. But when I execute the function, the file does not completely upload to the storage i.e. original file size is 10 KB and uploaded file size is 9 B. I'm using the following code:
const storageRef = firebase.storage().ref();
const dpRef = storageRef.child("users/dp.jpg").put(file, {
contentType: "image/jpeg",
});
dpRef.on(
firebase.storage.TaskEvent.STATE_CHANGED, // or 'state_changed'
(snapshot) => {
// Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log("Upload is " + progress + "% done");
switch (snapshot.state) {
case firebase.storage.TaskState.PAUSED: // or 'paused'
console.log("Upload is paused");
break;
case firebase.storage.TaskState.RUNNING: // or 'running'
console.log("Upload is running");
console.log(progress);
break;
default:
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
console.log("Unauthorized");
break;
case "storage/canceled":
// User canceled the upload
console.log("Cancelled");
break;
// ...
case "storage/unknown":
// Unknown error occurred, inspect error.serverResponse
console.log("Unknown");
break;
default:
break;
}
},
() => {
// Upload completed successfully, now we can get the download URL
dpRef.snapshot.ref.getDownloadURL().then((downloadURL) => {
console.log("File available at", downloadURL);
});
}
);
The console.log(progress) shows NaN for some reason. I also tried download
I'm not sure what is the problem, but I did this recently and I can share my code:
import firebase from 'firebase/app';
import 'firebase/firestore';
import 'firebase/storage';
const uploadImageFetch = async (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 imageNameBefore = uri.split('/');
const imageName = imageNameBefore[imageNameBefore.length - 1];
const ref = firebase.storage().ref().child(`images/${imageName}`);
const snapshot = await ref.put(blob);
blob.close();
return await snapshot.ref.getDownloadURL();
};
This function accept a local URI with the image, and returns the download URL.
You can see more examples in the official Documentation https://firebase.google.com/docs/storage/web/upload-files
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");
}
}
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);
}
};