Search code examples
node.jsmongodbmongoosemulter

MongoDB & Multer - How do I convert a buffer binary to an image?


I'm trying to make a simple website where I can upload an image to MongoDB and return it. All was going well until I got to the returning the image portion.

Right now, I can upload an image to the website using multer, when I upload the image do the database, it gets converted into binary data. Could anyone please show me how I would be able to convert the binary data to an image? that'll greatly appreciated!!

Schema & Model

const mongoose = require('mongoose');

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

const fileStorageOne = mongoose.model("fileStorageOne", fileStorageOneSchema);
module.exports = fileStorageOne

Multer Storage

const fileStorageOne = multer.diskStorage({
    destination: './uploads/fileOne/',
    filename: (req,file,cb)=> {
        cb(null, file.fieldname + ' - ' + Date.now() + path.extname(file.originalname))
    }
})

Express Upload to MongoDB

app.post('/upload_filestorageone', (req,res)=> {
    uploadFileStorageOne(req, res, (err)=> {
        if(err) {
            console.log(err);
            res.redirect('/')
        } else {
            const newImage = new fileStorageOneModel({
                img: {
                    data: fs.readFileSync(path.join(__dirname + '/uploads/fileOne/' + req.file.filename)),
                    contentType: 'image/png'
                }
            })
            newImage.save((err)=> {
                if(err) {
                    console.log(err);
                    res.redirect('/')
                } else {
                    console.log('Successfully uploaded a photo!');
                    res.redirect('/filestorageone')
                }
            })
        }
    })
})

Express Return the Image

app.get('/filestorageone/:id', (req,res)=> {
    fileStorageOneModel.findOne({}, (err,data)=> {
        if(err) {
            console.log(err)
        } else {
            const image = new Buffer(data.img.data).toString('base64')
            return res.json(image)
        }
    })
})

EJS Upload

<!DOCTYPE html>
<html>
    <head>
        <title><%= title %></title>
    </head>
    <body>
        <header>
            <%- include('./partials/header') %>
        </header>
        <%- include('./partials/version') %>

        <div class="submit-image-style">
            <form action="upload_filestorageone" method="POST" enctype="multipart/form-data">
                <input type="file" name="fileStorageOneImages">
                <button type="submit">Submit</button>
            </form>
        </div>
        <div class="output-style">
                <% image.forEach((image)=> { %>
                    <a href="/filestorageone/<%= image._id %>"><%= image._id %></a>
                <% }) %>
        </div>
    </body>
</html>

Sorry for the messy code, still pretty new to it!


Solution

  • Instead of sending the image with the .json() method, you'll want to set the appropriate content type so that the browser knows that it's an image, then just .send() the buffer:

    app.get("/filestorageone/:id", (req, res) => {
      fileStorageOneModel.findOne({}, (err, data) => {
        if (err) {
          console.log(err);
        } else {
          return res.type(data.img.contentType).send(data.img.data);
        }
      });
    });
    

    Here I'm using .type() which can also look up the correct mime type from a file extension, but you could just as well use .header('Content-Type', data.img.contentType).

    I've also left out the new Buffer() call since it's already a Buffer.

    As a side-note, assuming you're using multer and DiskStorage, you don't need to construct the path with path.join() and req.file.filename, as the full path can be found from req.file.path. You can also use req.file.mimetype so that you get the correct content type for also non-PNG images.