I have a custom hook called useBase64 where I convert my image to Base64. This hook accepts an initial value and returns handleCreateBase64 function and logo (piece of state). handleCreateBase64 converts the file to base64 and stores it in logo. I am sending an array of images as File and then mapping through that array and I want to store it in logo and then send it to that component. right now the problem is that in the logo I can only see 1 base64 even if I select multiple files.
useBase64 hook: -
import { useCallback, useState } from "react";
export const useBase64 = (initialValue) => {
const [logo, setLogo] = useState(initialValue)
const handleCreateBase64 = useCallback(async (receivedFile) => {
let file = receivedFile;
if(initialValue instanceof Array) {
file = Object.values(receivedFile)
file.map(async el => {
const base64 = await convertToBase64(el)
setLogo([...logo, base64])
})
}
});
const convertToBase64 = (file) => {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
if (!file) {
console.log("no image");
} else {
fileReader.readAsDataURL(file);
fileReader.onload = () => {
resolve(fileReader.result);
};
}
fileReader.onerror = (error) => {
reject(error);
};
});
};
return { handleCreateBase64, logo }
}
The result: -
As you can see above that I have 3 images selected but I see only 1 base64. I am quiet new to this so it will be great if full explanation is provided.
Wrong use of map and setState
Try:
const items = await Promise.all(file.map(el => convertToBase64(el)))
setLogo([...logo, ...items])
Related
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 :)
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
I am in React hooks trying to utilise code in this question Stackoverflow question- but, as I think someone is pointing out, there are problems implementing this in a local environment.
I have a variable containing my src path:
import avatar from '../../Assets/Photos/avatar.png'
Then I try to set the photo as a useState state:
const [ photo, setPhoto ] = useState(avatar)
Then I use useEffect to wait until the variable has loaded:
useEffect(() => {
if (photo) {
console.log("PHOTO EXISTS!!!!!!!!!!!!!!", photo)
console.log(convertImgToBase64())
}
}, [photo])
And the convertImgToBase64 function is almost identical to the one in the question I referenced:
function convertImgToBase64()
{
var canvas = document.createElement('CANVAS');
let img = document.createElement('avatar');
img.src = photo;
img.onload = function()
{
canvas.height = img.height;
canvas.width = img.width;
var dataURL = canvas.toDataURL('image/png');
canvas = null;
return dataURL
};
}
This returns undefined - why is this please?
Create an image state:
const[image, setImage] = useState("")
Function to convert to base64:
const convertBase64 = (file) => {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onload = () => {
resolve(fileReader.result);
};
fileReader.onerror = (error) => {
reject(error);
};
});
};
Set your image state to base64 string image:
const handleFileRead = async (event) => {
const file = event.target.files[0];
const base64 = await convertBase64(file);
setImage(base64);
};
<input type="file" multiple accept="image/gif, image/jpeg, image/png" onChange={(e) => handleFileRead(e)} />
I am using react-dropzone plugin for file uploads. I worried how i can convert each file to base64:
eg:
Here is my function where i get files:
I am creating here for example thumb for each file and attach to object. But how add to item here prop like base64string: and it will keep base64 data for each file?
this.onDrop = files => {
files.map(file =>
Object.assign(file, {
preview: URL.createObjectURL(file),
})
);
};
check this out,you can get files and then you can store them into images array of state instance.
onDropHandler = (files) => {
files.map(file => {
const reader = new FileReader();
reader.onload = (event) => {
//store result into your state array.
this.setState(prevState => {
const updatedImages = [...prevState.images, event.target.result];
return {
images: updatedImages,
}
})
console.log(event.target.result);
};
reader.readAsDataURL(file);
});
}
With this code you get an array with the base64 data and blob url for the preview.
const [data, setData] = useState([])
const onDrop = (files) => {
files.map((file) => {
const reader = new FileReader()
reader.onload = (e) => {
setdata(
files.map((file) =>
Object.assign(file, {
preview: URL.createObjectURL(file),
base64Image: e.target.result,
})
)
)
}
reader.readAsDataURL(file)
})
}
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(()=>{
});