Search code examples
node.jsmongoosemultersharpmulter-gridfs-storage

How can I update my images with multer nodeJS(The images are stored in mongodb)


I am creating the backend server for a ecommerce website. I handle file uploads with multer and store them in gridfs. Now, when updating a new product, not all images may be submitted to be updated. I need a way to identify what field was updated, so I can delete the current file and upload the new file to mongodb. I've reached the point where I think it's impossible, but I'm still trying. Are there any alternative approach I could take. All responses are greatly appreciated.

Here is the code for the router and the 3 middleware functions I call when a request is made to said route.

```
router
  .route("/:id")
  .put(
    productController.readPhotos,
    productController.appendPhotoOnRequestBody,
    productController.updateProduct
  )

exports.readPhotos = upload.fields([
  { name: "coverPhoto", maxCount: 1 },
  { name: "colorPhoto", maxCount: 4 },
]);



exports.appendPhotoOnRequestBody = asyncHandler(async (req, res, next) => {
  req.addedFiles = [];
  if (
    req.originalUrl === "/api/products" &&
    req.method === "POST" &&
    !req.files
  ) {
    // Error message to send to user if the above condition is true.
    const message = `To create a new product, a coverPhoto and at least one color with a colorPhoto must be specified`;
    return next(new AppError(400, message));
  } else if (!req.files) next();
  if (req.files?.coverPhoto) {
    // Extract coverPhoto
    const [coverPhoto] = req.files.coverPhoto;

    // Process images
    const coverPhotoBuffer = await processImage(coverPhoto.buffer, [300, 300]);

    // Pushing the coverPhoto to the db
    const coverPhotoStream = Readable.from(coverPhotoBuffer);
    const coverPhotoValue = await pushToDbFromStream(
      coverPhotoStream,
      req,
      coverPhoto
    );
    req.body.coverPhoto = coverPhotoValue.filename;
    req.addedFiles.push(req.body.coverPhoto);
  }
  if (req.files?.colorPhoto) {
    // Extract colorPhotos
    const colorPhotos = [...req.files.colorPhoto];
    const colorPhotoBuffers = await Promise.all(
      colorPhotos.map((photo) => processImage(photo.buffer, [50, 50]))
    );

    // Pushing the colorPhotos to the db
    const colorPhotosStreams = colorPhotoBuffers.map((buff) =>
      Readable.from(buff)
    );
    const colorPhotoValues = await Promise.all(
      colorPhotosStreams.map((stream, index) =>
        pushToDbFromStream(stream, req, colorPhotos[index])
      )
    );
    req.body.colors = JSON.parse(req.body.colors);
    colorPhotoValues.forEach((value, index) => {
      req.body.colors[index].colorPhoto = value.filename;
    });
    req.body.colors.forEach((color) => req.addedFiles.push(color.colorPhoto));
  }

  next();
});
exports.updateProduct = asyncHandler(async (req, _, next) => {
  req.product = await Product.findByIdAndUpdate(req.params.id, req.body, {
    runValidators: true,
    new: true,
  });
  next();
});

exports.updateProduct = asyncHandler(async (req, _, next) => {
  req.product = await Product.findByIdAndUpdate(req.params.id, req.body, {
    runValidators: true,
    new: true,
  });
  next();
});
```

Solution

  • There is a reference to the images in a color property of the product Schema. That uses the embedded document style. Instead, I could use the referenced way instead, and create new Color documents and update them individually.