Upload image in firebase expo react native - javascript

i have an issue, i trying solve a several hours but white not success, my issue consists in create an upload to firestorage in firebase and show your respective uri in a variable i called image, but i don't know what wrong in my code, why he save my image like this
and don't show you uri in my variable
bellow is my code
This is a create user i set here in imageUri a uploadImage who i called in my variable image
const createUser = async () => {
const imageUri = uploadImage()
await addDoc(
usersCollectionRef,
{
name: name || null,
cep: cep || null,
logradouro: logradouro || null,
numero: numero || null,
bairro: bairro || null,
uf: uf || null,
image: imageUri || null,
},
navigation.navigate("HomeScreen")
);
};
here is a code i pick image and my upload function to set a image in firestore (i follow the docs)
const pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
console.log(result);
if (!result.cancelled) {
setImage(result);
}
};
const uploadImage = () => {
const storageRef = ref(storage, `images/${image.name}`);
uploadBytes(storageRef, image).then((snapshot) => {
console.log(snapshot);
});
};
i really need some help for solve this issue i appreciate every tryed of help, any more questions about my code please comment if need i send my repository.

There are some mistakes in your code.
First, the ImagePicker returns an object like that (docs) :
{
"cancelled":false,
"height":1611,
"width":2148,
"uri":"file:///data/user/0/host.exp.exponent/cache/cropped1814158652.jpg"
}
There is no "name" field, that's why you see that undefined, because you access ${image.name}.
Second, as far as ImagePicker returns an object, you can't use it on uploadBytes pretending it is a Blob or similar. You should create a Blob from the uri image:
const response = await fetch(imageUri)
const blobFile = await response.blob()
Definetely, this is a function I suggest you to use for uploading an image on firebase storage
export async function uploadImage(imageUri) {
try {
const response = await fetch(imageUri)
const blobFile = await response.blob()
const reference = ref(storage, "your_name.jpg")
const result = await uploadBytes(reference, blobFile)
const url = await getDownloadURL(result.ref)
return url
} catch (err) {
return Promise.reject(err)
}
}

Related

Firebase storage delete image by downloadURL V9

I'm trying to delete an image from Firebase storage in React Native by downloadURL. Also i'm using web V9.
Writing down most essential chuncks of code:
import {
deleteObject,
getStorage,
ref
} from "firebase/storage";
...
// DownloadURL
const photo = "https://firebasestorage.googleapis.com.... ";
const storage = getStorage();
...
const storageRef = ref(storage, photo);
deleteObject(storageRef)
.then(() => {
console.log("File deleted successfully");
})
.catch((error) => {
console.log(error.message);
});
My code is inspired from here and I got this error:
[Unhandled promise rejection: FirebaseError: Firebase Storage: The
operation 'deleteObject' cannot be performed on a root reference,
create a non-root reference using child, such as .child('file.png').
(storage/invalid-root-operation)]
I'm little confused.
Later edit:
My function which upload the image and get the download url:
// uri is returned from "expo-image-picker"
const uploadImageAsync = async (uri) => {
// manipulateAsync imported from 'expo-image-manipulator'
const manipResult = await manipulateAsync(uri, [], {
compress: 0.6,
format: "jpeg",
});
const response = await fetch(manipResult.uri);
const blob = await response.blob();
const imageName = Date.now().toString();
const fileRef = ref(storage, `avatar/${user.uid}/${imageName}.jpg`);
const result = await uploadBytes(fileRef, blob);
getDownloadURL(fileRef).then((snapshot) => {
// downloadURL is added to Redux state to be used later
dispatch(addPhoto(snapshot));
});
};

Why does app crash when page starts to load due to useEffect hook with firebase upload in React Native

I'm trying to upload a photo to firebase storage in my post screen/page. I thought I was supposed to use an useEffect hook when calling api's so I did that and put in a dependency array for the two states that had to change for rerender. When I delete the useEffect and just have the uploadImage function and then I run the uploadImage function in the imagePicker function after confirming the event wasn't cancelled, it works but I can't seem to get the folder/imagename right. I can only call it something and then it continues to overwrite.
the workflow should be render the two buttons on screen, when the photo picker button is pressed, run the chooseFromLibrary function which will set the state for imageFilename and imageSelected(uri). That should tell the dependency array to run the effect of calling the uploadImage function with the variables for imageSelected and imageFilename. I want the ref in the uploadImage function to create/put in the folder for user.username (this is from context) and then the image name to be be imageFilename but it doesn't seem to be working.
What am I doing wrong?
const [user] = useContext(UserContext);
const [imageSelected, setImageSelected] = useState("");
const [imageFilename, setImageFilename] = useState("");
useEffect(() => {
uploadImage(imageSelected, imageFilename)
.then(() => {
Alert.alert("Success");
})
.catch((error) => {
Alert.alert(error);
});
}, [imageFilename, imageSelected]);
const uploadImage = async (uri, name) => {
const response = await fetch(uri);
const blob = await response.blob();
var ref = firebase
.storage()
.ref()
.child(`${user.username}` + name);
return ref.put(blob);
};
const chooseFromLibrary = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
if (!result.cancelled) {
let fileArray = result.uri.split("/");
let name = fileArray.pop();
setImageFilename(name);
setImageSelected(result.uri);
}
}
I had to run an if statement returning the effect if an image hadn't been selected yet.
if (!imageSelected) {
return;
}

Upload .vhd as Page-Blob to azure-blob-storage from Url

i have a bunch of VHD files stored on a private Server, which are accessible through a url.
I am trying upload these vhd files directly to my azure storage account using the azure javascript npm libraries. The vhds have to be uploaded as page-blobs. I tried using the method uploadPagesFromURL() of the pageblobClient but with no success. My code looks roughly like this:
async function uploadVHD(accessToken, srcUrl)
{
try {
// Get credentials from accessToken
const creds = new StorageSharedKeyCredential(storageAccount.name, storageAccount.key);
// Get blobServiceClient
const blobServiceClient = new BlobServiceClient(`https://${storageAccount.name}.blob.core.windows.net`, creds);
// Create Container
const containerClient = blobServiceClient.getContainerClient("vhd-images");
await containerClient.createIfNotExists();
const src = srcUrl.replace('https://', 'https://username:password#');
// Upload to blob storage
const pageBlobClient = containerClient.getPageBlobClient("Test.vhd");
// Get fileSize of vhd
const fileSize = (await axiosRequest(src, { method: "HEAD" })).headers["content-length"];
const uploadResponse = await pageBlobClient.uploadPagesFromURL(src, 0, 0, fileSize);
return uploadResponse;
} catch (error) {
return error;
}
});
It is not possible to upload the Page Blob with your URL directly. You need to read data from the url. Then upload using uploadPages method.
axios.get(URL, {
responseType: 'arraybuffer'
})
.then((response) => {
console.log(response.data)
console.log(response.data.length)
// upload page blob...
}).catch((error) => {
//handle error
});
// uploadPages method
const uploadResponse = pageBlobClient.uploadPages(data, 0, dataLength);

React FirebaseError DocumentReference.set()

Please can someone help me. I think the way I am handling the promise is wrong and really need someone to help me.
I am letting the user upload a picture . When the user presses submit the image is uploaded to firebase-storage. However I don't think I am handling the wait period to upload the image and setting the data to firebase-database. What I mean is when I press submit I get the error FireBase Function DocumentReference.set() called with invalid data Because it is setting the image to undefined
However if I wait a couple of seconds I get the console.log("File available at" + downloadUrl) which means the image was uploaded.
Basically I just need to add a waiting period to my code between when the image is uploaded and when to send the data to the firebase-database
This is my code any help will be much appreciated !!!!!
const uploadImage = async (uri, imageName) => {
const response = await fetch(uri)
const blob = await response.blob()
var ref = firebase.storage().ref().child(`images/${imageName}`)
ref.put(blob)
.then(()=>{
// Upload completed successfully, now we can get the download URL
var storageRef = firebase.storage().ref('images/' + imageName)
storageRef.getDownloadURL().then((downloadUrl)=>{
console.log(`File available at ${downloadUrl}`)
setDownload(JSON.stringify(downloadUrl))
})
})
.catch(error => {
setRefreshing(false) // false isRefreshing flag for disable pull to refresh
Alert.alert("An error occured", "Please try again later")
});
}
const handleSubmit = useCallback(() => {
if (postImage !== undefined) {
const fileExtention = postImage[0].split('.').pop()
const fileName = `${uniqid}.${fileExtention}`
uploadImage(postImage, fileName)
firebase.firestore()
.collection('Posts')
.doc(uniqid)
.set({
id: currentUser,
name: postName[0],
image: downloadImage,
})
}
})
Thank you in advance for all your help!!!!!
To use await inside useCallback you can try to wrap the code inside it in a self invoking function like this:
const handleSubmit = useCallback(() => {
(async () =>{ if (postImage !== undefined) {
const fileExtention = postImage[0].split('.').pop()
const fileName = `${uniqid}.${fileExtention}`
uploadImage(postImage, fileName)
await firebase.firestore()
.collection('Posts')
.doc(uniqid)
.set({
id: currentUser,
name: postName[0],
image: downloadImage,
})
}
})()
})

Firebase Cloud Function Too Slow

I have a cloud function that receives the uid of an image and associate it to the user who calls it after validating its dimensions and generate its thumbnail. It looks simple but I have to wait around 40 seconds to see the results, and sometimes it gets congested or something and I have to call the function again to see previous results.
Has anyone experience it before? How can I fix that?
exports.validateImageDimensions = functions
.region("us-central1")
.runWith({ memory: "2GB", timeoutSeconds: 120 })
.https.onCall(async (data, context) => {
As you can see the CPU used is high...
Thanks.
UPDATE
Code of the function:
exports.validateImageDimensions = functions
.region("us-central1")
.runWith({ memory: "2GB", timeoutSeconds: 120 })
.https.onCall(async (data, context) => {
// Libraries
const admin = require("firebase-admin");
const sizeOf = require("image-size");
const url = require("url");
const https = require("https");
const sharp = require("sharp");
const path = require("path");
const os = require("os");
const fs = require("fs");
// Lazy initialization of the Admin SDK
if (!is_validateImageDimensions_initialized) {
admin.initializeApp();
is_validateImageDimensions_initialized = true;
}
// Create Storage
const storage = admin.storage();
// Create Firestore
const firestore = admin.firestore();
// Get the image's owner
const owner = context.auth.token.uid;
// Get the image's info
const { id, description, location, tags } = data;
// Photos's bucket
const bucket = storage.bucket("bucket");
// File Path
const filePath = `photos/${id}`;
// Get the file
const file = getFile(filePath);
// Check if the file is a jpeg image
const metadata = await file.getMetadata();
const isJpgImage = metadata[0].contentType === "image/jpeg";
// Get the file's url
const fileUrl = await getUrl(file);
// Get the photo dimensions using the `image-size` library
https.get(url.parse(fileUrl), (response) => {
let chunks = [];
response
.on("data", (chunk) => {
chunks.push(chunk);
})
.on("end", async () => {
// Check if the image has valid dimensions
let dimensions = sizeOf(Buffer.concat(chunks));
// Create the associated Firestore's document to the valid images
if (isJpgImage && hasValidDimensions(dimensions)) {
// Create a thumbnail for the uploaded image
const thumbnailPath = await generateThumbnail(filePath);
// Get the thumbnail
const thumbnail = getFile(thumbnailPath);
// Get the thumbnail's url
const thumbnailUrl = await getUrl(thumbnail);
try {
await firestore
.collection("posts")
.doc(owner)
.collection("userPosts")
.add({
id,
uri: fileUrl,
thumbnailUri: thumbnailUrl, // Useful for progress images
description,
location,
tags,
date: admin.firestore.FieldValue.serverTimestamp(),
likes: [], // At the first time, when a post is created, zero users has liked it
comments: [], // Also, there aren't any comments
width: dimensions.width,
height: dimensions.height,
});
// TODO: Analytics posts counter
} catch (err) {
console.error(
`Error creating the document in 'posts/{owner}/userPosts/' where 'id === ${id}': ${err}`
);
}
} else {
// Remove the files that are not jpeg images, or whose dimensions are not valid
try {
await file.delete();
console.log(
`The image '${id}' has been deleted because it has invalid dimensions.
This may be an attempt to break the security of the app made by the user '${owner}'`
);
} catch (err) {
console.error(`Error deleting invalid file '${id}': ${err}`);
}
}
});
});
/* ---------------- AUXILIAR FUNCTIONS ---------------- */
function getFile(filePath) {
/* Get a file from the storage bucket */
return bucket.file(filePath);
}
async function getUrl(file) {
/* Get the public url of a file */
const signedUrls = await file.getSignedUrl({
action: "read",
expires: "01-01-2100",
});
// signedUrls[0] contains the file's public URL
return signedUrls[0];
}
function hasValidDimensions(dimensions) {
// Posts' valid dimensions
const validDimensions = [
{
width: 1080,
height: 1080,
},
{
width: 1080,
height: 1350,
},
{
width: 1080,
height: 750,
},
];
return (
validDimensions.find(
({ width, height }) =>
width === dimensions.width && height === dimensions.height
) !== undefined
);
}
async function generateThumbnail(filePath) {
/* Generate thumbnail for the progressive images */
// Download file from bucket
const fileName = filePath.split("/").pop();
const tempFilePath = path.join(os.tmpdir(), fileName);
const thumbnailPath = await bucket
.file(filePath)
.download({
destination: tempFilePath,
})
.then(() => {
// Generate a thumbnail using Sharp
const size = 50;
const newFileName = `${fileName}_${size}_thumb.jpg`;
const newFilePath = `thumbnails/${newFileName}`;
const newFileTemp = path.join(os.tmpdir(), newFileName);
sharp(tempFilePath)
.resize(size, null)
.toFile(newFileTemp, async (_err, info) => {
// Uploading the thumbnail.
await bucket.upload(newFileTemp, {
destination: newFilePath,
});
// Once the thumbnail has been uploaded delete the temporal file to free up disk space.
fs.unlinkSync(tempFilePath);
});
// Return the thumbnail's path
return newFilePath;
});
return thumbnailPath;
}
});
Pd: In the console I can read this record:
"Function execution took 103 ms, finished with status code: 200"
but I have to wait, as I said before, around 40 seconds to see the new doc on my firestore
You're not dealing with promises correctly. A callable function must return a promise that:
Resolves when all of the async work is complete
Resolves with the data to send back to the client
Right now, your function returns nothing, so it returns to the caller immediately, and the future of the async work that you kicked off is uncertain.
Note that https.get() is asynchronous and returns immediately, before its callback is invoked. You will need to find a way to instead return a promise that resovles when all of the callback's work is complete. (Consider that there are other HTTP client libraries that make it easier to get a promise instead of having to deal with callbacks.)

Categories

Resources