skip to Main Content

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 🙂

3

Answers


  1. When passing in variables in the following assignment:

    let image = `data:{image.img.contentType};base64,$(data.img.data.toString('base64'))`;
    

    You have to use ${ } instead of $( )

    let image = `data:${image.img.contentType};base64,${data.img.data.toString('base64')}`;
    

    Assuming image.img.contentType is also a variable in that scope

    Login or Signup to reply.
  2. 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;
    
    Login or Signup to reply.
  3. Somehow this method did not work for me. Working with the "buffers" did work though.

    Here is my setup:

    1. Stored image as a Buffer type in mongodb schema
    classImg: {
      data: Buffer,
      contentType: String
    },
    
    1. installed "buffers" to manipulate buffers.
    npm i buffers
    
    1. import { Buffer } from 'buffer';

    2. Then just use Buffer.

    <img width="300" height="300" src={`data:${item.classImg.contentType};base64, ${Buffer.from(item.classImg.data).toString('base64')}`}></img>
    

    I tried working with blob for a bit but all the browser displayed was a broken link instead of the image. Not sure why but didn’t dig much after I found this solution.
    -> EDIT: Broken link was because of my own stupidity – incorrect field name..uff.
    Just to add, I replaced this to use Amazon S3 bucket for images and files as fetching images from DB was taking up considerable time. Since mine is a customer facing B2C website, any second of time taken to load the front page was unacceptable, hence this.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search