how to upload file like pdf or doc in firestore - reactjs

I want to upload document files in firestore using redux. i get the file in and passed it as a state to the action file with other datas. following is my code in action file.
const createJob = (project) => {
return (dispatch, getState, {getFirebase, getFirestore}) => {
const firestore = getFirestore();
firestore.collection('Jobs').add({
...project,
postedby:'Employer1',
Documents:project.Documents.name
}).then(()=>{
dispatch({type:'CREATE_JOB', project});
}).catch((err)=>{
dispatch({type:'CREATE_JOB_ERROR', err});
})
}
};
but the data is saved as C:\fakepath\doc1.doc
how to upload the actual file in firestore

Technically you can upload an image to firestore. You need to convert it to base64 text first. Just spent some time figuring it out. I take the selected file from an upload file from browser then I upload it in the callback of the file reader. Hopefully this helps someone out.
function getBase64(file){
var n = document.getElementById('nameU').value;
//name of uploaded file from textbox
var d = document.getElementById('dateU').value;
//date of uploaded file from textbox
var reader = new FileReader();
reader.onerror = function (error) {
console.log('Error: ', error);
};
reader.readAsDataURL(file);
reader.onload = function () {
let encoded = reader.result.split(',');
//you split this to get mimetype out of your base64
addForSale(Date.now().toString(10), {uDesc: n, date: d, uFile: encoded[1]});
// I just used a timestamp as the ID
}
};
function addForSale(id, data) {
var collection = firebase.firestore().collection('forsale');
return collection.doc(id).set(data);}

Hi You cannot directly store the image in firestore.What you have to do is first store the document in firebase storage and get the url as reponse.Once the response is received add the url in documents.
First create an Storage Action in reduce:
import { storage } from '../../../firebase/firebase';
import {
ADD_DOCUMENT_STARTED,
ADD_DOCUMENT_COMPLETED,
ADD_DOCUMENT_ERROR
} from '../../actionTypes/storageActionTypes';
import { toast } from 'react-toastify';
import constants from '../../../config/constants';
export const addDocumentStart = () => ({
type: ADD_DOCUMENT_STARTED
});
export const addDocumentSuccess = () => ({
type: ADD_DOCUMENT_COMPLETED
});
export const addDocumentFailure = () => ({
type: ADD_DOCUMENT_ERROR
});
export const addDocument = (values, pathName) => {
const toastId = toast('Uploading Attachment, Please wait..', {
autoClose: false
});
return (dispatch) =>
new Promise(function(resolve, reject) {
dispatch(addDocumentStart());
const timeStamp = new Date().getTime();
const image = values.document[0];
var name;
if (values && values.documentName) {
name = timeStamp.toString().concat(values.documentName);
} else {
name = timeStamp.toString().concat(image.name);
}
const imageUpload = storage.ref(`${pathName}/${name}`).put(image);
imageUpload.on(
'state_changed',
(snapshot) => {
switch (snapshot.state) {
case 'paused':
reject('Upload is paused');
dispatch(addDocumentFailure('Upload is paused'));
break;
}
},
(error) => {
switch (error.code) {
case 'storage/unauthorized':
reject('Permission Denied');
dispatch(addDocumentFailure('Permission Denied'));
break;
case 'storage/canceled':
reject('Upload Cancelled');
dispatch(addDocumentFailure('Upload Cancelled'));
break;
case 'storage/unknown':
reject('Server Response Error');
dispatch(addDocumentFailure('Server Response Error'));
break;
}
},
() => {
toast.update(toastId, {
render: 'Attachment Uploaded successfully',
type: toast.TYPE.SUCCESS,
autoClose: constants.toastTimer
});
storage
.ref(pathName)
.child(name)
.getDownloadURL()
.then((url) => {
dispatch(addDocumentSuccess());
resolve(url);
});
}
);
});
};
Then in your onsubmit:
this.props.dispatch(addDocument(values, 'Jobs')).then((resp) => {
let documentUrl = resp;
firestore.collection('Jobs').add({
...project,
postedby:'Employer1',
Documents:documentUrl
}).then(()=>{
});

Related

Firebase form saves url to database on second click.For the first click it saves empty url in database,React

I am new to the firebase database and trying to upload a form that has an image file and video file and save the downloaded URL to real-time database at the same time. On submit form for the first time the image file and video file are saved to storage but the URLs are empty in the database. On a second click, the image and video files are uploaded and saved correctly. I don't understand what is happening on the first click. Here is a truncated part of my code that handles the upload and saving to firebase.
const thumnailUpload = async () => {
try {
const storage = getStorage();
const storageRef = ref_storage(
storage,
`products_test/${Date.now() + title}`
);
const uploadTask = uploadBytesResumable(storageRef, thumbnail);
uploadTask.on(
"state_changed",
(snapshot) => {
const progress =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log("Thumbnail Upload is " + progress + "% done");
switch (snapshot.state) {
case "paused":
console.log("Thumbnail Upload is paused");
break;
case "running":
console.log("Thumbnail Upload is running");
break;
}
},
(error) => {
toast.error(error.message);
},
async () => {
let downloadUrlThumbnail1 = await getDownloadURL(
uploadTask.snapshot.ref
);
setThumbnailUrl(downloadUrlThumbnail1);
console.log("Thumbnail Upload is completed");
console.log(thumbnailUrl);
toast.success("Thumbnail Upload is completed");
}
);
} catch (err) {
console.log(err);
}
};
const handleVideoUpload = async () => {
try {
const storage = getStorage();
const storageRef = ref_storage(
storage,
`products_test/${Date.now() + title}`
);
const uploadTask = uploadBytesResumable(storageRef, mainContent);
uploadTask.on(
"state_changed",
(snapshot) => {
const progress =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log("Video Upload is " + progress + "% done");
switch (snapshot.state) {
case "paused":
console.log(" Video Upload is paused");
break;
case "running":
console.log("Video Upload is running");
break;
}
},
(error) => {
toast.error(error.message);
},
async () => {
let downloadUrlVideo = await getDownloadURL(uploadTask.snapshot.ref);
setContentUrl(downloadUrlVideo);
console.log("Video Upload is completed");
console.log(contentUrl);
toast.success("Video Upload is completed");
}
);
} catch (err) {
console.log(err);
}
};
const addProduct = async (e) => {
e.preventDefault();
handleVideoUpload();
thumnailUpload();
const writeUserData = async (
title,
description,
price,
category,
type,
thumbnailUrl,
contentUrl
) => {
const db = getDatabase();
await set(ref_database(db, "testproducts/"), {
title: title,
description: description,
contentType: type,
category: category,
price: price,
imgUrl: thumbnailUrl,
videoUrl: contentUrl,
uploader: currentUser.uid,
});
};
writeUserData(
title,
description,
price,
category,
type,
thumbnailUrl,
contentUrl
);
};
I have tried using async await but still not working as I had wanted.I am a beginner and I may be making an obvious mistake.Thanks

Converting BLOB (docxtemplater) to PDF - REACT

Hello everyone I am developing a website using React.js and Node.js where I generate a word document from the user's input and thats worked just fine with docxtemplater!
The problem is I want to let the user download the word document direclty as a PDF document but docxtemplater just permit us to save the file as a docx.
To convert the docx, i have the idea to save my blob document in mongodb with gridfs (already done and worked fine) and then get my blob and convert it to pdf (where i'm blocked)
Here is the part of my code for generate my docx and saving the generated word docx in mongodb (i have intentionnaly delete some things not important for this question)
import React, { useState, useEffect } from "react";
import Docxtemplater from "docxtemplater";
import PizZip from "pizzip";
import PizZipUtils from "pizzip/utils/index.js";
import { saveAs } from "file-saver";
import axios from "axios";
function loadFile(url, callback) {
PizZipUtils.getBinaryContent(url, callback);
}
export const DocxFile = ({ formData }) => {
const [file, setFile] = useState(null);
const [currentlyUploading, setCurrentlyUploading] = useState(false);
const [docxId, setDocxId] = useState(null);
const [progress, setProgress] = useState(null);
const [inputContainsFile, setInputContainsFile] = useState(false);
const [template, setTemplate] = useState();
const generateDocument = () => {
loadFile(template, function (error, content) {
if (error) {
throw error;
}
var zip = new PizZip(content);
var doc = new Docxtemplater(zip, {
paragraphLoop: true,
linebreaks: true,
});
doc.setData({
company: formData.general.companyClient,
version: formData.documentVersion,
quote: formData.general.quoteNumber,
HERE MY DATA //Working :)
});
try {
doc.render();
} catch (error) {
function replaceErrors(key, value) {
if (value instanceof Error) {
return Object.getOwnPropertyNames(value).reduce(function (
error,
key
) {
error[key] = value[key];
return error;
},
{});
}
return value;
}
if (error.properties && error.properties.errors instanceof Array) {
const errorMessages = error.properties.errors
.map(function (error) {
return error.properties.explanation;
})
.join("\n");
}
throw error;
}
var out = doc.getZip().generate({
type: "blob",
mimeType:
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
});
saveAs(
out,
`${name}.${documentVersion}.docx`
);
setFile(out);
setInputContainsFile(true);
});
};
const fileUploadHandler = () => {
const fd = new FormData();
fd.append("docx", file, file.name);
axios
.post(`api/docx/upload`, fd, {
onUploadProgress: (ProgressEvent) => {
setProgress((ProgressEvent.loaded / ProgressEvent.total) * 100);
console.log(
"upload progress",
Math.round((ProgressEvent.loaded / ProgressEvent.total) * 100)
);
},
})
.then(({ data }) => {
setDocxId(data);
setFile(null);
setInputContainsFile(false);
setCurrentlyUploading(false);
})
.catch((err) => {
console.log(err);
if (err.response.status === 400) {
const errMsg = err.response.data;
if (errMsg) {
console.log(errMsg);
}
} else {
console.log("other error", err);
setInputContainsFile(false);
setCurrentlyUploading(false);
}
});
};
const handleClick = () => {
if (inputContainsFile) {
setCurrentlyUploading(true);
fileUploadHandler();
}
};
return (
<>
<button
className="modify__button1 enregistrer generator__button"
onClick={generateDocument}
>
Generate the docx
</button>
<label htmlFor="file" onClick={handleClick}>
{file ? <>SUBMIT</> : <></>}
</label>
</>
);
};
This is what i got on mongodb after sending : MongodbBLOB
After that, i will do a get request to get my blob ( i already know how to get my blob) But then, how can i convert it to pdf (and keep the template used for my docx) ?
Update :
I found a package named docx-pdf which allow me to convert my docx to pdf.
But unfortunately, i've got a lot of webpack problems when i compiled (Can't resolve 'fs', 'child_process', 'path') because "webpack < 5 used to include polyfills for node.js core modules by default."
I will try to resolve this and will update this answer if i succeed to convert my docx :)

How to add additional info to an image?

I am new to React and Ionic and builded the Ionic React photo gallery app with the help of this ionic tutorial. Now I also want to add additional info like its date, its location, some inputfields,... to the image when it is displayed but I just did not figure out how.
This is the code from the tutorial:
import { useState, useEffect } from "react";
import { isPlatform } from '#ionic/react';
import { Camera, CameraResultType, CameraSource, Photo } from '#capacitor/camera';
import { Filesystem, Directory } from '#capacitor/filesystem'
import { Storage } from '#capacitor/storage'
import { Capacitor } from '#capacitor/core';
import internal from "assert";
import { defaultMaxListeners } from "stream";
const PHOTO_STORAGE = "photos";
export function usePhotoGallery() {
const [photos, setPhotos] = useState<UserPhoto[]>([]);
useEffect(() => {
const loadSaved = async () => {
const {value} = await Storage.get({key: PHOTO_STORAGE });
const photosInStorage = (value ? JSON.parse(value) : []) as UserPhoto[];
// If running on the web...
if (!isPlatform('hybrid')) {
for (let photo of photosInStorage) {
const file = await Filesystem.readFile({
path: photo.filepath,
directory: Directory.Data
});
// Web platform only: Load the photo as base64 data
photo.webviewPath = `data:image/jpeg;base64,${file.data}`;
}
}
setPhotos(photosInStorage);
};
loadSaved();
}, []);
const takePhoto = async () => {
const cameraPhoto = await Camera.getPhoto({
resultType: CameraResultType.Uri,
source: CameraSource.Camera,
quality: 100
});
const fileName = new Date().getTime() + '.jpeg';
const savedFileImage = await savePicture(cameraPhoto, fileName);
const newPhotos = [savedFileImage, ...photos];
setPhotos(newPhotos);
Storage.set({key: PHOTO_STORAGE,value: JSON.stringify(newPhotos)});
};
const savePicture = async (photo: Photo, fileName: string): Promise<UserPhoto> => {
let base64Data: string;
// "hybrid" will detect Cordova or Capacitor;
if (isPlatform('hybrid')) {
const file = await Filesystem.readFile({
path: photo.path!
});
base64Data = file.data;
} else {
base64Data = await base64FromPath(photo.webPath!);
}
const savedFile = await Filesystem.writeFile({
path: fileName,
data: base64Data,
directory: Directory.Data
});
if (isPlatform('hybrid')) {
// Display the new image by rewriting the 'file://' path to HTTP
// Details: https://ionicframework.com/docs/building/webview#file-protocol
return {
filepath: savedFile.uri,
webviewPath: Capacitor.convertFileSrc(savedFile.uri),
};
}
else {
// Use webPath to display the new image instead of base64 since it's
// already loaded into memory
return {
filepath: fileName,
webviewPath: photo.webPath
};
}
};
const deletePhoto = async (photo: UserPhoto) => {
// Remove this photo from the Photos reference data array
const newPhotos = photos.filter(p => p.filepath !== photo.filepath);
// Update photos array cache by overwriting the existing photo array
Storage.set({key: PHOTO_STORAGE, value: JSON.stringify(newPhotos) });
// delete photo file from filesystem
const filename = photo.filepath.substr(photo.filepath.lastIndexOf('/') + 1);
await Filesystem.deleteFile({
path: filename,
directory: Directory.Data
});
setPhotos(newPhotos);
};
return {
deletePhoto,
photos,
takePhoto
};
}
export interface UserPhoto {
filepath: string;
webviewPath?: string;
}
export async function base64FromPath(path: string): Promise<string> {
const response = await fetch(path);
const blob = await response.blob();
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onerror = reject;
reader.onload = () => {
if (typeof reader.result === 'string') {
resolve(reader.result);
} else {
reject('method did not return a string')
}
};
reader.readAsDataURL(blob);
});
}
I managed to get the date and location with the Geolocation Plugin but I have no clue how to bind it to the taken photo...
I assume that I have to add the desired data via function base64FromPath() for web and const file = await Filesystem.readFile() for mobile? And afterwards display with the export interface UserPhoto part but I am completly lost and do not know where to start. Any suggestions welcome!
I found a way might not be the prettiest or directest but it seems to work for me.
Within the takePhoto function I get the location and date and then pass it on via the savePicture function. Inside the savePicture function I return latitude, longitude and time and therefore can access it through the loadSaved function within the photo object. Inside interface UserPhoto the 3 variables need also to be declared.
Here is the updated code if somebody would like to make a suggestion:
import { useState, useEffect } from "react";
import { isPlatform } from '#ionic/react';
import { Geolocation, Geoposition } from '#ionic-native/geolocation';
import { Camera, CameraResultType, CameraSource, CameraDirection, Photo } from '#capacitor/camera';
import { Filesystem, Directory } from '#capacitor/filesystem'
import { Storage } from '#capacitor/storage'
import { Capacitor } from '#capacitor/core';
import { stringify } from "querystring";
const PHOTO_STORAGE = "photos";
export function usePhotoGallery() {
const [photos, setPhotos] = useState<UserPhoto[]>([]);
useEffect(() => {
const loadSaved = async () => {
const {value} = await Storage.get({key: PHOTO_STORAGE });
const photosInStorage = (value ? JSON.parse(value) : []) as UserPhoto[];
// If running on the web...
if (!isPlatform('hybrid')) {
for (let photo of photosInStorage) {
const file = await Filesystem.readFile({
path: photo.filepath,
directory: Directory.Data
});
// Web platform only: Load the photo as base64 data
photo.webviewPath = `data:image/jpeg;base64,${file.data}`;
}
}
setPhotos(photosInStorage);
};
loadSaved();
}, []);
const takePhoto = async () => {
const position = await Geolocation.getCurrentPosition();
const latitude = position.coords.latitude
const longitude = position.coords.longitude
const time = new Date(position.timestamp).toLocaleString()
const cameraPhoto = await Camera.getPhoto({
resultType: CameraResultType.Uri,
source: CameraSource.Camera,
direction: CameraDirection.Rear,
quality: 100
});
const fileName = new Date().getTime() + '.jpeg';
const savedFileImage = await savePicture(cameraPhoto, fileName, latitude, longitude, time);
const newPhotos = [savedFileImage, ...photos];
setPhotos(newPhotos);
Storage.set({key: PHOTO_STORAGE,value: JSON.stringify(newPhotos)});
};
const savePicture = async (photo: Photo, fileName: string, latitude: number, longitude: number, time:string): Promise<UserPhoto> => {
let base64Data: string;
// "hybrid" will detect Cordova or Capacitor;
if (isPlatform('hybrid')) {
const file = await Filesystem.readFile({
path: photo.path!
});
base64Data = file.data;
} else {
base64Data = await base64FromPath(photo.webPath!);
}
console.log("base64Data")
console.log(base64Data)
let savedFile = await Filesystem.writeFile({
path: fileName,
data: base64Data,
directory: Directory.Data
});
console.log(savedFile)
if (isPlatform('hybrid')) {
// Display the new image by rewriting the 'file://' path to HTTP
// Details: https://ionicframework.com/docs/building/webview#file-protocol
return {
filepath: savedFile.uri,
webviewPath: Capacitor.convertFileSrc(savedFile.uri),
latitude: latitude,
longitude: longitude,
time: time
};
}
else {
// Use webPath to display the new image instead of base64 since it's
// already loaded into memory
return {
filepath: fileName,
webviewPath: photo.webPath,
latitude: latitude,
longitude: longitude,
time: time
};
}
};
const deletePhoto = async (photo: UserPhoto) => {
// Remove this photo from the Photos reference data array
const newPhotos = photos.filter(p => p.filepath !== photo.filepath);
// Update photos array cache by overwriting the existing photo array
Storage.set({key: PHOTO_STORAGE, value: JSON.stringify(newPhotos) });
// delete photo file from filesystem
const filename = photo.filepath.substr(photo.filepath.lastIndexOf('/') + 1);
await Filesystem.deleteFile({
path: filename,
directory: Directory.Data
});
setPhotos(newPhotos);
};
return {
deletePhoto,
photos,
takePhoto
};
}
export interface UserPhoto {
filepath: string;
webviewPath?: string;
latitude: number;
longitude: number;
time: string;
}
export async function base64FromPath(path: string): Promise<string> {
const response = await fetch(path);
const blob = await response.blob();
//const blob = new Blob(neu)
return new Promise((resolve, reject) => {
const reader = new FileReader();
console.log(reader)
reader.onerror = reject;
reader.onload = () => {
if (typeof reader.result === 'string') {
resolve(reader.result);
} else {
reject('method did not return a string')
}
};
reader.readAsDataURL(blob);
});
}
To access is in my UI.tsx I use eg {photo.latitude} to display the taken latitude value

React native problem saving data to firestore

Here is my code.
import FirebaseKeys from "./config";
import firebase from 'firebase/app';
class Fire {
constructor() {
firebase.initializeApp(FirebaseKeys);
}
addPost = async ({text, localUri}) => {
const remoteUri = await this.uploadPhotoAsync(localUri)
return new Promise((res, rej) => {
this.firestore.collection("posts").add({
text,
uid: this.uid,
timestamp: this.timestamp,
image: remoteUri
})
.then(ref => {
res(ref)
})
.catch(error => {
rej(error)
});
});
};
uploadPhotoAsync = async uri => {
const path = `photos/${this.uid}/${Date.now()}.jpg`
return new Promise(async (res, rej) => {
const response = await fetch(uri)
const file = await response.blob()
let upload = firebase.storage().ref(path).put(file)
upload.on(
"state_changed",
snapshot => {},
err => {
rej(err)
},
async () => {
const url = await upload.snapshot.ref.getDownloadURL();
res(url);
}
);
});
};
What I have noticed is there is no problem uploading image on uploadImageAsync but when it comes to addPost method it does not create a collection named "post".
what should I do with this? thankyou in advance. btw I am not using expo on this.
First of all, the collection that your addPost method is trying to create is called "posts", not "post". When it comes to Firestore, always check that your security rules allow for this user to write to that path.
It would also be nice to see the usage of addPost, and see what its res and rej return.

Why is async/await not having any effect here?

So I have these two functions - One is an upload function (uploads file to DB). And the other function needs the url's of the uploaded files and for some reason I'm not able to wait for the upload function to finish so I can get the url's of the images uploaded so I can store them in my database.
the problem is at
uploadedImages.map(async file => {
urls.push(await uploadedFiles(file, uid, key))
// console.log(urls)
})
In the following code. Line 12.
export const handleUploads = (uploadedImages, uid, key, directory) => {
return (dispatch, getState, {
getFirebase,
getFirestore
}) => {
const firestore = getFirestore();
//const uid = getState().firebase.auth.uid;
let urls = []
uploadedImages.map(async file => {
urls.push(await uploadedFiles(file, uid, key))
// console.log(urls)
})
return firestore.collection('Uploads').doc(key).set({
uid: uid,
urls: urls,
timestamp: new Date(),
directory: directory
}).then(() => {
dispatch({
type: 'UPLOAD_SUCCESS'
});
toastr.success('הודעה', 'עודכן בהצלחה')
console.log(key)
}).catch(err => {
dispatch({
type: 'UPLOAD_ERROR'
}, err);
toastr.error('אופס! אירעה שגיאה')
});
}
};
Heres the upload function itself:
export const uploadedFiles = (file, uid, key) => {
// const firestore = getFirestore();
// const key = firestore.ref().child(property.uid).push().key
// const img = storage.ref().child(property.uid).child(key)
// const uploadedImages = property.uploadedImages
//let uploaded_arr = []
const uploadTask = storage.ref(`Properties/${uid}/${key}/${file.name}`).put(file);
uploadTask.on('state_changed',
(snapshot) => {
// progrss function ....
// const progress = Math.round((snapshot.bytesTransferred / snapshot.totalBytes) * 100);
//
// console.log(progress);
},
(error) => {
// //error function ....
// dispatch({ type: 'UPLOAD_ERROR'}, error);
toastr.error('הודעת מערכת', error)
return ''
},
() => {
// //complete function ....
storage.ref('Properties').child(String(uid)).child(String(key)).child(file.name).getDownloadURL().then(url => {
// console.log(url)
return url
})
});
}
A solution would be highly appreciated.
P.S there is no error, its just that the await doesn't await the function to execute.
It simply has no effect.
uploadedFiles should return a promise for your await to work. Await makes javascript to wait until the promise returns with a result. Now your uploadedFiles below resolves url if it successfully gets updated in the database else, it gives out empty string.
Hence, update your uploadedFiles like so...
export const uploadedFiles = (file, uid, key) => {
return new Promise(function(resolve, reject) {
// const firestore = getFirestore();
// const key = firestore.ref().child(property.uid).push().key
// const img = storage.ref().child(property.uid).child(key)
// const uploadedImages = property.uploadedImages
//let uploaded_arr = []
const uploadTask = storage.ref(`Properties/${uid}/${key}/${file.name}`).put(file);
uploadTask.on('state_changed',
(snapshot) => {
// progrss function ....
// const progress = Math.round((snapshot.bytesTransferred / snapshot.totalBytes) * 100);
//
// console.log(progress);
},
(error) => {
// //error function ....
// dispatch({ type: 'UPLOAD_ERROR'}, error);
toastr.error('הודעת מערכת', error)
reject()
},
() => {
// //complete function ....
storage.ref('Properties').child(String(uid)).child(String(key)).child(file.name).getDownloadURL().then(url => {
// console.log(url)
resolve(url);
})
});
});
}
This is because map will execute all the async lambdas immediately after each other. If you have n element in that array, then map executes n async functions without waiting for the promises being finished.
You can do that using Promise.all. Here the example.
export const handleUploads = (uploadedImages, uid, key, directory) => {
return async (dispatch, getState, { getFirebase, getFirestone }) => {
// ...
const urls = await Promise.all(
uploadedImages.map(async file => uploadedFiles(file, uid, key))
);
// ...
return ...
}
}
Where uploadedFiles needs to be async, too.

Resources