1

I have a struggle with displaying an images, retrieved from mongoDB, where it is saved as a binary data. I'm using Multer for storing images to database. Images are stored into database correctly, but when I try to display them and covert them to base64, I've got this error: Cannot read property 'toString' of undefined...

Here is my code:

var storage = multer.diskStorage({
 destination: './public/uploads/',
 filename: (req, file, cb) => {
     cb(null, file.fieldname + '-' + Date.now())
 }
});

var upload = multer({storage: storage});

router.post('/', upload.single('image'), async (req, res, next) => {
 console.log(res);
 req.book = new Book();
 next();
}, postOrEditBook('new'));

function postOrEditBook(page) {
 return async (req, res) => {
     let book = req.book;
     book.img = {
         data: fs.readFileSync(path.join( './public/uploads/' + req.file.filename)),
         contentType: 'image/png'
     }
     try {
         book = await book.save();
         if(page == 'new') {
             res.redirect('/list');
         } else {
             res.redirect(`/books/${book.slug}`);
         }
     } catch (e) {
         res.render(`articles/${page}`, { book: book })
     }
 }
}

In schema images are stored like this:

const bookSchema = new mongoose.Schema({
    img: {
        data: Buffer,
        contentType: String
    }
    
});

Input field for the file:

<form action="/books" method="POST" autocomplete="off" enctype="multipart/form-data">
      <input type="file" name="image"">
</form>

Image into mongodb looks like this: img: Object :Binary('iVBORw0KGgoAAAANSUhEUgAABVYAAAMACAIAAABAXKuVAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMA...', 0) contentType: "image/png"

And, I've tried to display images in ejs file like this:

<div>
      <% books.forEach(book => { %>
            <img src="data:book/<%= book.img.contentType %>;base64, 
            <%= book.img.data.toString('base64') %>">
      <% }) %>
</div>


Can anyone tell me what I'm doing wrong? I really can't figure out what the problem is. I can't find anything that would help me solve this problem...

1 Answer 1

1

I was having the same problem while creating my application. I think we followed the same tutorial because this is exactly what was happening to me. So, after many hours of troubleshooting, I was finally able to figure out that the problem was being caused by an object in my database. Before I added the multer package and the image upload functionality, I was testing a database that stored posts with two fields that each contained a string.

const blogPostSchema = new mongoose.Schema({
  title: String,
  content: String
});

I saved a test object containing these two fields in my database. I believe that the code added to render the images in my ejs page was trying to loop through my database, and when it encountered that first object that had no reference to the image object and its respective data fields, it was causing the error as it was trying to read an undefined property in the schema. Deleting that item from the database using Robo3T fixed the issue and I was able to render the images properly. I would try removing these objects from your database if you have any, and make sure that the documents in your database all include the image object as well as all the other information you want.

This is what my schema looked like after I added the image object:

const blogPostSchema = new mongoose.Schema({
  title: String,
  content: String,
  img: {
    data: Buffer,
    contentType: String
  }
});

The server code:

require("dotenv").config();
const express = require("express");
const bodyParser = require("body-parser");
const ejs = require("ejs");
const mongoose = require("mongoose");
const session = require("express-session");
const passport = require("passport");
const passportLocalMongoose = require("passport-local-mongoose");
const GoogleStrategy = require("passport-google-oauth20").Strategy;
const FacebookStrategy = require("passport-facebook");
const findOrCreate = require("mongoose-findorcreate");
const fs = require("fs");
const path = require("path");
const multer = require("multer");

const app = express();

const storage = multer.diskStorage({
  destination: "./public/uploads",
  filename: function(req, file, cb) {
    cb(null, file.fieldname + "-" + Date.now());
  }
});

const upload = multer({storage: storage});

app.use(express.static("public"));
app.set("view engine", "ejs");
app.use(bodyParser.urlencoded({extended: true}));

mongoose.connect("mongodb://localhost:27017/LfdBlogDB", {
  useNewUrlParser: true
});

const blogPostSchema = new mongoose.Schema({
  title: String,
  content: String,
  img: {
    data: Buffer,
    contentType: String
  }
});

const BlogPost = new mongoose.model("BlogPost", blogPostSchema);

app.get("/blog", function(req, res) {
  BlogPost.find({}, function(err, foundPosts) {
    if(err) {
      console.log(err);
    } else {
      if (foundPosts) {
        res.render("blog", {blogPosts: foundPosts});
      }
    }
  });
});

app.post("/blog", upload.single('postImage'), function(req, res) {
  const title = req.body.title;
  const content = req.body.content;
  const imgType = req.file.mimetype;
  const imgData = fs.readFileSync(path.join(__dirname + '/public/uploads/' + req.file.filename));

  BlogPost.create({title: title, content: content, img: {data: imgData, contentType: imgType}}, function(err){
    if (err) return handleError(err);
    res.redirect("/blog");
  });
});

And the ejs page:

<%- include('partials/header') %>

    enter code here

<h1>Posts</h1>

<!-- Render All Posts -->
<div class="container posts-container">
  <% blogPosts.forEach((post) => { %>
      <img class="post-img-preview" src="data:image/<%=post.img.contentType%>;base64,<%=post.img.data.toString('base64')%>" alt="">
      <h3><%=post.title%></h3>
      <p><%=post.content%></p>
  <% }); %>
</div>

<!-- Submit New Post -->
<form class="" action="/blog" method="post" enctype="multipart/form-data">
  <label for="title">Title:</label>
  <input type="text" name="title">
  <label for="content">Write something:</label>
  <input type="text" name="content">
  <label for="postImage">Upload image:</label>
  <input type="file" name="postImage" required>
  <button type="submit" name="button">Submit</button>
</form>

<%- include('partials/footer') %>
Sign up to request clarification or add additional context in comments.

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.