I convert images into base64 strings before sending them to a NodeJS server. Then, I store base64 strings into MongoDB. The problem is that when I try to get my base64 strings from the database, it takes an extremely long time (about 1 minute for 10 images). And my website deals with a lot of images.
Here is my React code :
import React, { Component, useState, useCallback } from "react"
import Header from '../Header'
import ModalCountry from '../ModalCountry'
import Photo from './Photo'
import Gallerie from './Gallerie';
import ReactFileReader from 'react-file-reader'
import Config from '../../Config'
import './styles.css'
class Photos extends Component {
constructor(props){
super(props)
this.state = {
countryAlpha3: this.props.location.state ? this.props.location.state.lookedAlpha3 : "",
photos: [],
showAddPhotos: "d-none",
photosToAdd: []
}
this.handleFiles = this.handleFiles.bind(this);
this.storePhotos = this.storePhotos.bind(this);
}
handleFiles(files){
let photosStamp = [];
files.base64.forEach(function(file){
photosStamp.push({
photo: file,
base64: file,
title: "Titre",
description: "Description"
})
})
this.setState({
photosToAdd: photosStamp
}, () => console.log(this.state.photosToAdd))
this.setState({
showAddPhotos: 'd-block'
})
}
componentDidMount(){
var photosArray = this.state.photos;
let self = this;
fetch(Config.apiUrl + 'photos_thisUser/' + this.state.countryAlpha3, {
method: 'GET',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
"Authorization": localStorage.getItem("xsrfToken")
}
})
.then(response => response.json())
.then(body => {
body.forEach(function(data){
self.setState({
photos: self.state.photos.concat({
photo: data.base64,
caption: data.title,
subcaption: data.description
})
})
})
})
.catch(err => {
console.log(err)
})
}
storePhotos(){
fetch(Config.apiUrl + 'addPhotos', {
method: 'PUT',
credentials: 'include',
body: JSON.stringify({
'photos': this.state.photosToAdd,
'alpha3': this.state.countryAlpha3
}),
headers: {
'Content-Type': 'application/json',
"Authorization": localStorage.getItem("xsrfToken")
}
})
.then(response => response.json())
.then(body => {
this.setState({
photos: this.state.photos.concat(this.state.photosToAdd),
showAddPhotos: "d-none",
photosToAdd: []
})
})
.catch(err => {
console.log(err)
})
}
render() {
return (
<div className="mainPhotos">
{this.state.redirection}
<div className="profileHeader">
<Header openModal={this.getDataFromSearchBar} />
</div>
<ModalCountry ref={this._child} modalTitle={this.state.modalTitle} alpha3={this.state.alpha3} />
<div className="header">
<div className="banner col-md-12 row">
<div className="col-md-12 titlePhotos text-center">
Photos de {this.state.countryName}
</div>
</div>
</div>
<div className="photosContent">
<div className="text-center">
<ReactFileReader fileTypes={[".png", ".jpg", ".jpeg", ".bmp"]} base64={true} multipleFiles={true} handleFiles={this.handleFiles}>
<button className="btn btn-info">Ajouter des photos</button>
</ReactFileReader>
</div>
<div className={"photosToAdd row text-center " + (this.state.showAddPhotos)}>
<div className="photosFull photosToAddWithButton">
{this.state.photosToAdd.map((item, index) => <Photo src={item.photo} openLightbox={() => {console.log("opened")}} />)}
</div>
<div className="col-md-12 text-center">
<button
className="btn btn-success form-control btnValid col-md-1"
onClick={this.storePhotos}
>
Ajouter
</button>
<button
className="btn btn-danger form-control btnCancel col-md-1"
onClick={this.cancelAddPhotos}
>
Annuler
</button>
</div>
</div>
<div className="photosContent row">
<div className="photosFull">
{this.state.photos.map((item, index) => <Photo src={item.photo} openLightbox={() => {this.setState({
activePhotoIndex: index
}, this.setState({
galleryIsVisible: true
}))}} />)}
<Gallerie photos={this.state.photos} activePhotoIndex={this.state.activePhotoIndex} galleryIsVisible={this.state.galleryIsVisible} close={() => this.setState({
galleryIsVisible: false
})} />
</div>
</div>
</div>
</div>
)
}
}
export default Photos
Here is my nodeJS code :
var express = require('express')
var router = express.Router()
const Photo = require('../../models/Photo')
const Country = require('../../models/Country')
let mongoose = require('mongoose')
router.put('/addPhotos', function(req, res, next){
if(req.body.alpha3 && req.body.photos){
Country.findOne({ alpha3: req.body.alpha3 })
.then(country => {
console.log(req.body.photos)
req.body.photos.forEach(function(photo){
photoToAdd = new Photo({
country: mongoose.Types.ObjectId(country._id),
user: mongoose.Types.ObjectId(req.decoded.id),
base64: photo.base64,
title: photo.title,
description: photo.description
});
photoToAdd.save(function(err, addedPhoto){
})
})
{
res.json({
success: true,
message: "Photos added"
})
}
})
}
else
{
res.json({
error: req.body.alpha3
})
}
})
router.get('/photos_thisUser/:alpha3', function(req, res, next){
Country.findOne({ alpha3: req.params.alpha3 })
.then(country => {
Photo.find({ user: req.decoded.id, country: country._id }, {_id: 0, country: 0})
.then(photos => {
res.json(photos)
})
.catch(err => {
res.status(400).send({
success: false,
message: "Error: " + err
})
})
})
.catch(err => {
res.status(400).send({
success: false,
message: "Error: " + err
})
})
})
module.exports = router
Here is the mongoose schema :
let mongoose = require('mongoose');
const PhotoSchema = new mongoose.Schema({
country: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Country'
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
base64: {
type: String,
required: true
},
title: {
type: String
},
description: {
type: String
}
});
let PhotoModel = mongoose.model('Photo', PhotoSchema);
module.exports = PhotoModel
Maybe I shouldn't store base64 data into a string in mongo? I would like to display images one by one, then the user won't have to wait as long. But how can I update only a part of the state? Or is it the wrong technique to use base64 in my website?