Search code examples
node.jsreactjsmongodbmern

How to display image in ReactJS from MongoDB


I am currently building a drawing app using React, Node and MongoDB.

It saves images along with name and username in the database. When home page is opened, it has to retrieve and display the image on the screen.

The image is stored in database in the form of a buffer and while displaying it on screen, I am converting it to base64 (found it in some article).

When I try to display it in imagesPage.ejs present inside the node_app for testing purpose, it is properly displaying the image but when I try to do it inside the react component, it is giving the following error:

GET data:image/{image.img.contentType};base64,$(data.img.data.toString('base64')) net::ERR_INVALID_URL

and when I change the image url by removing that extra "image/" before {image.img.contentType}, i get this error :

Failed to load resource: net::ERR_INVALID_URL

react_app part :

I am fetching data in CardData.js and it is fetching it correctly as I verified using console.log :

import React, { useState } from "react";
import Axios from "axios";

export default function CardData(){
    const [data, setData] = useState({
        lst: []
    });

    const url = `http://localhost:5000/getDrawings/${localStorage.getItem("user")}`;
    Axios.get(url).then((res)=>{
        setData(res.data);
    }).catch(err=>console.log(err));

    return data;
} 

Card.js to display the image ( used try...catch to display remaining page even if there's an error with the image) :

import React, { useEffect, useState } from "react";
import CardData from "./CardData";

function Card(){
    
    const cards = CardData();
    try{const allCards = cards.map( function (data) {
        //let username = data.username;
        console.log("here!!!");
        let name = data.name;
        let image = `data:{image.img.contentType};base64,$(data.img.data.toString('base64'))`;

        return( 
            <div className = "col-3">
                <div className = "adjust">
                    <div className="image">
                        <img width="300" height="300" src={image}></img>
                    </div>
                    <div className="name">{name}</div>
                </div>
            </div>
        );
    })
    return [allCards];}
    catch(e){ return null;}
}

export default Card;

node_app part :

imageModel.js contains the mongoose schema:

const Mongoose = require('mongoose')

const imageSchema = Mongoose.Schema({
    name: {
        type: String,
        default: ""
    },
    username: {
        type: String,
        default: ""
    },
    img:
    {
        data: Buffer,
        contentType: {
            type: String,
            default: 'image/png'
        }
    }
}); 

module.exports = Mongoose.model('Image',imageSchema);

router.js contains the routes :

const express = require('express')
const router = express.Router()
//const imgModel = require('../models/imageModel')
const { 
        // other components.
        getDrawings,
} = require('../controllers/controllers')
const imgModel = require('../models/imageModel');

//other router.post and router.get
router.get('/getDrawings/:username',getDrawings);

module.exports = router;

controllers.js contains the getDrawings function:

//all necessary imports

const getDrawings = async (req, res) => {
    const username = req.params.username;
    const items = await imgModel.find({username : username});

    //to display imagesPage.ejs I uncomment this and comment out res.send(items)
    //res.render('imagesPage',{items : items});

    res.send(items); 
}

//also exports other functions but removed it from here.
module.exports = {getDrawings};

imagesPage.ejs which correctly displays the image ( it is also used to add images to database, but that is not my current problem ) :


<!DOCTYPE html>
<html lang="en">
  
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Image Uploading</title>
</head>
  
<body>
 
    <h1>Uploaded Images</h1>
    <div>
        <% items.forEach(function(image) { %>
        <div>
            <div>
                <img src="data:image/<%=image.img.contentType%>;base64,
                     <%=image.img.data.toString('base64')%>" style="width:300px;height: 300px">
                <div>
                    <h5><%= image.name %></h5>
                </div>
            </div>
        </div>
        <% }) %>
    </div>
</body>
  
</html>

the react page correctly displays the name of the image and remaining page but doesn't display the image and gives the error mentioned above, whereas imagesPage.ejs correctly displays everything. Please help me out.

Thank you :)


Solution

  • so, as the server is sending a JSON image, you need to convert JSON to Buffer with Int8Array

    then that needs to be converted to Blob, and then create a URL object:

    EDIT: use useEffect when fetching, that's what's giving that loop error

    try it now:

    import React, { useState, useEffect } from "react";
    import Axios from "axios";
    
    export default function CardData(){
        const [data, setData] = useState(
            []
        );
    
    
    useEffect(() => {
    
        const url = `http://localhost:5000/getDrawings/${localStorage.getItem("user")}`;
        Axios.get(url).then((res)=>{
            setData(res.data);
        }).catch(err=>console.log(err));
      }, []);
    
        return data;
    } 
    
    import React, { useEffect, useState } from "react";
    import CardData from "./CardData";
    
    function Card(){
        
        const cards = CardData();
        try{const allCards = cards.map( function (data) {
            //let username = data.username;
            console.log("here!!!");
            const name = data.name;
    
            const blob = new Blob([Int8Array.from(data.img.data.data)], {type: data.img.contentType });
    
            const image = window.URL.createObjectURL(blob);
    
            return( 
                <div className = "col-3">
                    <div className = "adjust">
                        <div className="image">
                            <img width="300" height="300" src={image}></img>
                        </div>
                        <div className="name">{name}</div>
                    </div>
                </div>
            );
        })
        return [allCards];}
        catch(e){ return null;}
    }
    
    export default Card;