2

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 3

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;
Sign up to request clarification or add additional context in comments.

12 Comments

I'm not getting the error now but it is still not displaying the image. I can also see 100s of "here!!!" in the console from the console.log("here!!!") in Card.js
@amaan well, how many cards/images do you have, it means it's 100s... try deleting all the data and save a few fresh examples
I have only 1 image saved. It is not displaying the image but still displaying a lot of those "here!!!" in console.
@amaan then you're looping something else.., it should log only once. on the server console.log items, and on the frontend console.log cards. it should be the same, an array with a single item
@amaan great. whoa, that's a lot of questions.. like said, server converts everything to JSON, including image buffer, which is now on client-side an object like {type:'Buffer', data:'..'} (hence data.img.data.data), so on the browser it needs to be converted back to buffer, and then to a blob to enable browser to handle it. try logging every step from server to client to get a better picture
|
0

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

4 Comments

oh how silly of me to forget that !!!
now the error is gone but the image is still not displayed. Even the image name is gone. I can see 100s of " here !!! " in the console.
what is the value of image.img.contentType?
it is "image/png"
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.