Search code examples
node.jsmongodbexpressherokuejs

Load an image from Public Folder using string from MongoDB


I am trying to load images that are uploaded from a form to an index page. I have my public folder in app.js file mapped correctly and it is structured so that any image would be located at "/public/assets/img/filename.ext"

The schema I have for blog posts that are throwing errors is:

// schema setup
var mongoose = require("mongoose");

var postSchema = new mongoose.Schema({
    title: String,
    image: String,
    uploadedImage: {
        data: Buffer,
        contentType: String,
    },
    dest: String,
    postdata: String,
    excerpt: String,
    tag: String,
    icon: String,
    color: String,
    featured: String,
    keywords: String,
    description: String,
    slug: String,
    create: {type: Date, default: Date.now},
    author: {
      id: {
         type: mongoose.Schema.Types.ObjectId,
         ref: "User"
      },
      username: String
   },
});

module.exports = mongoose.model("Posts", postSchema);

This schema is filled by a form that I created that has the following route structure:

require("dotenv").config();
const   express     =   require("express"),
        router      =   express.Router(),
        Posts       =   require("../models/blog"),
        upload      =   require("../middleware/upload"),
        path        =   require('path'),
        fs          =   require('fs'),
        middleware  =   require("../middleware"),
        slugify     =   require('slugify');

// CREATE
router.post("/", middleware.isLoggedIn, middleware.isAdmin, upload.single('uploadedImage'), function(req,res){
    var title =     req.body.title;
    var image =     req.body.image;
    var postdata =  req.body.postdata;
    var exceprt =   req.body.excerpt;
    var tag =       req.body.tag;
    var icon =      req.body.icon;
    var color =     req.body.color;
    var featured =  req.body.featured;
    var keywords    = req.body.keywords;
    var description = req.body.description;
    var dest        = req.file.filename;
    var slug        = slugify(title);
    var uploadedImage = {
            data: fs.readFileSync(path.join('./public/assets/img/' + req.file.filename)),
            contentType: 'image/png'
        };
    var author = {
        id: req.user._id,
        username: req.user.username
    };
    // link dB to post values
    var newPost= {title:title, slug:slug, image:image, postdata:postdata, excerpt:exceprt, tag:tag, icon:icon, color:color, featured:featured, author:author, keywords:keywords, description:description, dest:dest, uploadedImage:uploadedImage};
    Posts.create(newPost, function(err, newlyCreated) {
        if (err){
            console.log(err);
        }else{
            console.log("slug:" + slug);
            console.log(req.file.path);
            res.redirect("/blog");
        }
    });
});

And to serve it in my EJS I am calling it like so inside of my loop:

<% Posts.forEach(function(post){ %>
<img src="/assets/img/<%= post.dest %>" class="img-fluid" />
<% }); %>

The result is the right path to my public folder, but I get a 404 error every time that says the file does not exist. I have checked and the files do exist so I am a little lost as to why I am getting that error. I have tested at local level and AWS C9 console, but when I push to a staging site on Heroku I get the error.

Any suggestions or code corrections would be helpful.


Solution

  • Anyone who uses Heroku will have this problem until you upgrade to a paid dyno. See this Heroku Doc that shows that the free tier goes to sleep. Since their system is transient and when a dyno sleeps it clears all uploaded files, that was the cause of my images not showing up in the templates. I tested this, and confirmed with a sales rep that because they are trying to remove their free tier this setting is fixed and will result in loss of data. Hope this helps someone, cheers!