Not able to upload image on Firebase Storage - javascript

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

Related

Firebase Storage Image upload - Function to return the Image URL after uploading it

I need to implement this async function,
const uploadImage = async () => {
const filename = new Date().getTime() + photo!.name
const storage = getStorage(app)
const storageRef = ref(storage, filename)
const uploadTask = uploadBytesResumable(storageRef, photo!);
uploadTask.on('state_changed',
(snapshot) => {},
(error) => {
console.log("error while uploading photo", error)
},
async () => {
photoUrl = await getDownloadURL(uploadTask.snapshot.ref);
console.log("getDownloadURL", photoUrl)
return photoUrl
}
);
}
It is the function to upload images to Firebase-Storage. Here I need to return the "photoUrl ". I need to call the function like,
const res = await uploadImage(photo)
how do I do this? The uploaded image's URL should return from the function.
The object returned by uploadBytesResumable is also a promise, so you can just await that and then call getDownloadURL:
const uploadImage = async () => {
const filename = new Date().getTime() + photo!.name
const storage = getStorage(app)
const storageRef = ref(storage, filename)
const uploadTask = uploadBytesResumable(storageRef, photo!);
await uploadTask;
photoUrl = await getDownloadURL(uploadTask.snapshot.ref);
return photoUrl
}
You actually don't even need a reference to the task, as you already have the storageRef, the above can be shorted to:
const uploadImage = async () => {
const filename = new Date().getTime() + photo!.name
const storage = getStorage(app)
const storageRef = ref(storage, filename)
await uploadBytesResumable(storageRef, photo!);
return await getDownloadURL(storageRef);
}
Here is same thing to upload multiple files to firebase and return their URLs
async function uploadMultipleFilesToFirebase(imagesArray) {
try {
const requests = imagesArray.map(async (imageFile) => {
const storageRef = ref(storage, filename)
await uploadBytesResumable(storageRef, imageFile);
return await getDownloadURL(storageRef);
})
return Promise.all(requests)
} catch (error) {
throw({ error })
}
}
And then use it with:
urlsOfUploadedImages.value = await uploadProductToFirebase(productData)

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
}

Javascript promises + useState + firebase onSnapshot

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;
}
});

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)
}
}

having issues Setting Image caching in angular 6

Im using firebase storage, while uploading images to it i also pass in cacheControl pair. Im having trouble on getting the image to cache locally when i download.
Any help pls ..
doupload(event: FileList) {
const file = event.item(0);
if (file.type.split('/')[0] !== 'image') {
console.error('unsupported file type:(');
return;
}
const path = `test/${new Date().getTime()}_${file.name}`;
const fileRef = this.storage.ref(path);
const customMetadata = { app: 'Namma Tiruppur', cacheControl: 'public,max-age=150000', };
this.task = this.storage.upload(path, file, { customMetadata });
this.percentage = this.task.percentageChanges();
this.snapshot = this.task.snapshotChanges().pipe(
tap(snap => {
if (snap.bytesTransferred === snap.totalBytes) {
this.newsImg = path;
}
})
);
this.snapshot.pipe(finalize(() => this.downloadURL = fileRef.getDownloadURL())).subscribe();
}

Categories