简体   繁体   中英

How to store photo fast in React with API nodeJS

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?

Store your images as base64 encoded string is not a good idea since them would be approximately 37% larger than their original binary representation.
If your website is image based you should save your images somewhere.
As on premise solution you can store them in your server and then just save image path on your db, otherwise you can use a cloud solution , AWS S3 for instance, and then store and retrieve images using their api's.
If your website has a lot of images and visits, though them are not free, i suggest to use some cloud solution since them come (almost) out of the box with some cool features like caching and image manipulation and you don't have to take care of disk space or server performance. retrieve a (possibly cached) scaled image, would be always faster than base64 encoded, or a fullsize image saved on your server. Regarding the frontend you can use some react lazy-loading module to load and display your pictures when you need it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM