3

I'm working on creating an upload/download images feature in Node.js. So far there is the POST request which saves the image as binary in MongoDB and also a GET request which returns that image back. But I don't know how can I use that response, it is an array of numbers and don't know how to transform it.

Here is the Mongo Model:

image.js const mongoose = require('mongoose'); const Schema = mongoose.Schema;

const ImageItem = new Schema({
  id: {
    type: String
  },
  value: {
    type: Buffer
  },
});

module.exports = Image = mongoose.model('image', ImageItem);

the POST image which creates an entry in DB:

const image = require('../models/image');
const user = require('../models/user');

const multer = require('multer');
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, './uploads/');
  },
  filename: function (req, file, cb) {
    cb(null, file.originalname + new Date().toISOString());
  },
});


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

module.exports = function (app) {
  app.post('/upload', upload.single('value'), (req, res, next) => {
    const newImage = new image({
      id: req.body.id,
      value: req.file.path,
    });
    newImage
      .save()
      .then((result) => {
        console.log(result);
        res.status(201).json({
          message: 'created succesfully',
        });
      })
      .catch((err) => {
        console.log(err);
        res.status(500).json({
          error: err,
        });
      });
  });
};

and the entry created in DB:

enter image description here

for getting the image I created a GET request:

const image = require('../models/image');

module.exports = function (app) {
  app.get('/upload/:id', (req, res) => {
    console.log('req.body.id', req.params);
    image.find({ id: req.params.id }, function (err, results) {
      if (err) {
        res.send(`error: ${err}`);
      } else {
        res.send(results);
      }
    });
  });
};

which tested in Postman returns a JSON containing an array of numbers:

[
    {
        "_id": "5ebd1c112892f4230d2d4ab4",
        "id": "[email protected]",
        "value": {
            "type": "Buffer",
            "data": [
                117,
                112,
                108,
                111,
                97,
                100,
                115,
                47,
                117,
                115,
                101,
                114,
                80,
                105,
                99,
                116,
                117,
                114,
                101,
                46,
                112,
                110,
                103,
                50,
                48,
                50,
                48,
                45,
                48,
                53,
                45,
                49,
                52,
                84,
                49,
                48,
                58,
                50,
                51,
                58,
                49,
                51,
                46,
                57,
                51,
                52,
                90
            ]
        },
        "__v": 0
    }
]

How can I use this data to get the actual image?

2 Answers 2

1

You can create a Buffer from that array.

const imageBuffer = Buffer.from(row.value.data); // [117, 112, 108...]

In any case checking your ImageItem schema, row.value is going to be a Buffer.

Now all you need to do is set the correct content-type and respond the Buffer instead of the Mongoose schema, using res.send.

app.get('/upload/:id', (req, res) => {
    console.log('req.body.id', req.params);
    image.find({ id: req.params.id }, function (err, results) {
      if (err) {
        res.send(`error: ${err}`);
      } else {
        const [row] = results;
        res.header('Content-Type', 'image/png');
        res.send(row.value);
      }
    });
});

If you don't know the Content-Type you can use file-type package to get it from the Buffer.

const { mime } = fileType(row.value)

Since you're only getting a particular image you may want to use .findOne instead of .find


Now you have other issue, you're storing the file path, instead of the binary image which is what you want.

Those bytes you posted equals: uploads/userPicture.png2020-05-14T10:23:13.934Z"

const data = new TextDecoder().decode(new Uint8Array([117,112,108,111,97,100,115,47,117,115,101,114,80,105,99,116,117,114,101,46,112,110,103,50,48,50,48,45,48,53,45,49,52,84,49,48,58,50,51,58,49,51,46,57,51,52]))

console.log(data);

You have to save the actual image, not the file path for your code to work.

const fs = require('fs').promises;
// ....

const newImage = new image({
      id: req.body.id,
      value: await fs.readFile(req.file.path)
});
Sign up to request clarification or add additional context in comments.

6 Comments

where should const image from first line should be put?
renamed it to imageBuffer to avoid conflicts. It's placed on the route as you can see on the second snippet.
First of all thank you for your response but it is not working at the moment. I put that code and in Postman it returns this error message: Proxy error: Could not proxy request /upload/[email protected] from localhost:3000 to http://localhost:5000 (ECONNRESET). .I'm running my app from both client and server with nodemon. After trying to GET the image the server crashes: TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received undefined
and on the client side it says: Proxy error: Could not proxy request /upload/[email protected] from localhost:3000 to http://localhost:5000. See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (ECONNRESET). I'm using 5000 on server and 3000 on client
then you're not passing an array to BUffer.from
|
1

When you send your image to a client, mongoose calls the method "toJSON", by default the Buffer JSON object looks like you showed. You could overwrite the toJSON method for the mongoose scheme (here you find info - https://mongoosejs.com/docs/guide.html#toJSON) and your server will return a base64 representation of you image instead.

const mongoose = require("mongoose"),
  Schema = mongoose.Schema;

var imageSchema = new Schema({
  data: {
    type: Buffer,
    required: true,
  },
  type: {
    type: String,
    required: true,
  },
});

imageSchema.set("toJSON", {
  virtuals: true,
  versionKey: false,
  transform: function (doc, ret) {
    const base64 = doc.data.toString("base64");
    ret.data = `data:image${doc.type};base64,${base64}`;

    return ret;
  },
});

module.exports = mongoose.model("Image", imageSchema);

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.