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
}
Related
I am trying to upload an image to firebase storage, the problem is I get Firebase Storage: Invalid URL when I try to upload it.
First I get the uri from the react-native-image-picker then I use it to make the reference.
This is my code:
export async function uploadImage() {
const options = {
storageOptions: {
path: "images",
mediaType: "photo"
},
includeBase64: true
}
const result = await launchImageLibrary(options);
const imagePath = result.assets[0].uri
console.log(imagePath)
//firebase
const imageRef = ref(storage, imagePath);
const snapshot = await uploadBytes(imageRef, file, {
contentType: "image/jpeg",
});
console.log("uploaded!")
}
this is the uri printed by the console.log:
file:///data/user/0/com.chatapp/cache/rn_image_picker_lib_temp_f85b1089-267f-4271-9ccb-2f1487d83619.jpg
while uploading any to firebase storage
you should have permission to upload the file.
what do you want to upload the file or any Base64 content
const uploadImage = async () => {
const options: ImageLibraryOptions = {
storageOptions: {
path: 'images',
mediaType: 'photo',
},
includeBase64: true,
};
const result = await launchImageLibrary(options);
if (result) {
const {assets} = result;
if (assets && assets.length > 0) {
try {
const imagePath = result.assets[0].uri;
console.log(imagePath);
//firebase
const reference = storage().ref('black-t-shirt-sm.png');
const imageRef = await reference.putFile(imagePath, {
contentType: 'image/jpeg',
});
console.log('imageRef', imageRef);
// const snapshot = await uploadBytes(imageRef, file, {
// contentType: 'image/jpeg',
// });
console.log('uploaded!');
} catch (error) {
console.log('error', error);
}
}
}
};
for uploading the file you need to follow its guideline RN Firebase
storage upload requires platform specific code. I have code snippet at my github playground with storage upload and react-native-image-picker Snippet
Essentially
const uri = Platform.OS === 'android' ? uploadUri : uploadUri.replace('file://', '');
const task = storage().ref(ref);
return task.putFile(uri);
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
I have a database listener in my code and I am trying to get every user's new posts and then (when I have all of them in an array) update the posts state.
My code looks like this but it is not working good, because setPosts is async and sometimes it might be called again before ending the state update. I think that I need to wrap the listener in a Promise but I have no idea how to do it detaching the listener when the component unmounts.
useEffect(() => {
const { firebase } = props;
// Realtime database listener
const unsuscribe = firebase
.getDatabase()
.collection("posts")
.doc(firebase.getCurrentUser().uid)
.collection("userPosts")
.onSnapshot((snapshot) => {
let changes = snapshot.docChanges();
changes.forEach(async (change) => {
if (change.type === "added") {
// Get the new post
const newPost = change.doc.data();
// TODO - Move to flatlist On end reached
const uri = await firebase
.getStorage()
.ref(`photos/${newPost.id}`)
.getDownloadURL();
// TODO - Add the new post *(sorted by time)* to the posts list
setPosts([{ ...newPost, uri }, ...posts]);
}
});
});
/* Pd: At the first time, this function will get all the user's posts */
return () => {
// Detach the listening agent
unsuscribe();
};
}, []);
Any ideas?
Also, I have think to do:
useEffect(() => {
const { firebase } = props;
let postsArray = [];
// Realtime database listener
const unsuscribe = firebase
.getDatabase()
.collection("posts")
.doc(firebase.getCurrentUser().uid)
.collection("userPosts")
.orderBy("time") // Sorted by date
.onSnapshot((snapshot) => {
let changes = snapshot.docChanges();
changes.forEach(async (change) => {
if (change.type === "added") {
// Get the new post
const newPost = change.doc.data();
// Add the new post to the posts list
postsArray.push(newPost);
}
});
setPosts(postsArray.reverse());
});
But in this case, the post uri is saved too in the firestore document (something I can do because I write on the firestore with a cloud function that gets the post from storage), and I don't know if it is a good practice.
Thanks.
Update
Cloud Function code:
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) {
const serviceAccount = require("./serviceAccountKey.json");
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-name");
// 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
getImageFromUrl(fileUrl)
.then(async (image) => {
// Check if the image has valid dimensions
let dimensions = sizeOf(image);
// 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}`);
}
}
})
.catch((e) => {
console.log(e);
});
/* ---------------- 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 getImageFromUrl(uri) {
return new Promise((resolve, reject) => {
const options = url.parse(uri); // Automatically converted to an ordinary options object.
const request = https.request(options, (response) => {
if (response.statusCode < 200 || response.statusCode >= 300) {
return reject(new Error("statusCode=" + response.statusCode));
}
let chunks = [];
response.on("data", (chunk) => {
chunks.push(chunk);
});
response.on("end", () => {
try {
chunks = Buffer.concat(chunks);
} catch (e) {
reject(e);
}
resolve(chunks);
});
});
request.on("error", (e) => {
reject(e.message);
});
// Send the request
request.end();
});
}
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;
}
});
I'm trying to upload an Image to my firebase but I can't figure it out.
This is my code
_pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
quality: 1
});
if (!result.cancelled) {
this.uploadImage(result.uri, 'test-image')
.then(() => {
console.log('it work')
})
.catch(error => {
console.log('it does not work')
console.error(error)
})
}
};
uploadImage = async (uri, imageName) => {
const response = await fetch(uri);
const blob = response.blob();
const ref = firebase.storage().ref().child(`images/${imageName}`);
return ref.put(blob);
}
I'm getting this error.
FirebaseStorageError {
"code_": "storage/invalid-argument",
"message_": "Firebase Storage: Invalid argument in `put` at index 0: Expected Blob or File.",
"name_": "FirebaseError",
"serverResponse_": null,
}
Use this code:
`const blob = await response.blob();`
Instead of:
const blob = response.blob();
to solve your issue.
try var ref= firebase.storage().ref().child('images/'+imageName) instead of yours
I am trying to upload an image to firebase and then produce 2 thumbnails. I am able to do this with no problems. My current road block is when I write the urls to the realtime database, I am always getting the same url as the initial upload.
For example:
1st upload I get my uploaded image with the two proper thumbnails for the image
2nd upload I get my uploaded image with the two previous thumbnails (first image)
3rd upload I get my uploaded image with the first images thumbnails...
...this continues to reproduce the urls for the first upload
In my storage the correct thumbnails are being generated, but the urls are always for the first upload?
I don't know if this is a problem with the getSignedUrl() or not, really not sure whats going on here.
Here is my cloud function:
export const generateThumbs = functions.storage
.object()
.onFinalize(async object => {
const bucket = gcs.bucket(object.bucket); // The Storage object.
// console.log(object);
console.log(object.name);
const filePath = object.name; // File path in the bucket.
const fileName = filePath.split('/').pop();
const bucketDir = dirname(filePath);
const workingDir = join(tmpdir(), 'thumbs');
const tmpFilePath = join(workingDir, 'source.png');
if (fileName.includes('thumb#') || !object.contentType.includes('image')) {
console.log('exiting function');
return false;
}
// 1. ensure thumbnail dir exists
await fs.ensureDir(workingDir);
// 2. Download Sounrce fileName
await bucket.file(filePath).download({
destination: tmpFilePath
});
//3. resize the images and define an array of upload promises
const sizes = [64, 256];
const uploadPromises = sizes.map(async size => {
const thumbName = `thumb#${size}_${fileName}`;
const thumbPath = join(workingDir, thumbName);
//Resize source image
await sharp(tmpFilePath)
.resize(size, size)
.toFile(thumbPath);
//upload to gcs
return bucket.upload(thumbPath, {
destination: join(bucketDir, thumbName),
metadata: {
contentType: 'image/jpeg'
}
}).then((data) => {
const file = data[0]
// console.log(data)
file.getSignedUrl({
action: 'read',
expires: '03-17-2100'
}).then((response) => {
const url = response[0];
if (size === 64) {
// console.log('generated 64');
return admin.database().ref('profileThumbs').child(fileName).set({ thumb: url });
} else {
// console.log('generated 128');
return admin.database().ref('categories').child(fileName).child('thumb').set(url);
}
})
.catch(function (error) {
console.error(err);
return;
});
})
});
//4. Run the upload operations
await Promise.all(uploadPromises);
//5. Cleanup remove the tmp/thumbs from the filesystem
return fs.remove(workingDir);
})
Cleaned up my code and solved my problem, here is how I generated the urls and passed them to the proper URLs by accessing the users UID and postId in the file path:
export const generateThumbs = functions.storage
.object()
.onFinalize(async object => {
const fileBucket = object.bucket; // The Storage bucket that contains the file.
const filePath = object.name; // File path in the bucket.
const fileName = filePath.split('/').pop();
const userUid = filePath.split('/')[2];
const sizes = [64, 256];
const bucketDir = dirname(filePath);
console.log(userUid);
if (fileName.includes('thumb#') || !object.contentType.includes('image')) {
console.log('exiting function');
return false;
}
const bucket = gcs.bucket(fileBucket);
const tempFilePath = path.join(tmpdir(), fileName);
return bucket.file(filePath).download({
destination: tempFilePath
}).then(() => {
sizes.map(size => {
const newFileName = `thumb#${size}_${fileName}.png`
const newFileTemp = path.join(tmpdir(), newFileName);
const newFilePath = `thumbs/${newFileName}`
return sharp(tempFilePath)
.resize(size, size)
.toFile(newFileTemp, () => {
return bucket.upload(newFileTemp, {
destination: join(bucketDir, newFilePath),
metadata: {
contentType: 'image/jpeg'
}
}).then((data) => {
const file = data[0]
console.log(data)
file.getSignedUrl({
action: 'read',
expires: '03-17-2100'
}, function(err, url) {
console.log(url);
if (err) {
console.error(err);
return;
}
if (size === 64) {
return admin.database().ref('profileThumbs').child(userUid).child(fileName).set({ thumb: url });
} else {
return admin.database().ref('categories').child(fileName).child('thumb').set(url);
}
})
})
})
})
}).catch(error =>{
console.log(error);
});
})