Image Uploading and Attaching for the Blog Project

Today I have been working on this feature that allows users to upload image as attachments and to include them as Markdown format.

Here’s a demo of the complete feature:

The first decision I made was how I should accept the upload. After several minutes of thinking with my pea brain, I decided to use multer to take file upload from clients. multer will put the file upload in a directory with a randomly generated name to avoid name duplications. It works well with express js in that it sets the ‘file’ property on req with useful properties such as filename, filesize and the path to the file.

Without much further thinking (which later proved to be a mistake), I thought it would be natural to simply serve the directory that has all the uploaded files.

So I wrote these:

API for upload

const express = require('express');
const router = new express.Router();
const fs = require('fs');
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });
const auth = require('../auth');

const { ALLOWED_EXTENSIONS, MAX_SIZE } = require('../config');

router.post('/', [auth.admin, upload.single('upload')], (req, res, next) => {
  const filename = req.file.originalname;
  const path = req.file.path;

  const splitArr = filename.split('.');
  if (splitArr.length === 1 || !ALLOWED_EXTENSIONS.includes(splitArr.pop().toLowerCase())) {
    removeFile(path);
    return res.status(403).json({ message: 'Forbidden file extension' });
  }

  if (req.file.size > MAX_SIZE) {
    removeFile(path);
    return res.status(403).json({ message: `File exceeds maximum allowed size: ${MAX_SIZE / 1000000} MB` });
  }

  res.json({ path: req.file.path });
});

function removeFile(path) {
  fs.unlink(path, err => {
    if (err) console.log(err);
  });
}

module.exports = router;

serve directory:

app.use('/uploads', express.static('uploads'));

frontend’s upload method

onUpload() {
  if (!this.file) {
    this.props.displayMessage('You haven\'t selected any file yet!');
    return;
  }

  const data = new FormData();
  data.set('upload', this.file);
  instance.post('/uploads', data, {
    headers: { Authorization: 'Bearer ' + this.props.token },
  })
    .then(response => {
      const files = this.state.files.slice();
      files.push(response.data.path);
      this.setState({
        files,
      });
      console.log(files);
    })
    .catch(err => {
      this.props.displayMessage(err.response.data.message);
    });
}

This worked fine. However, when I deployed some other minor changes such as progress bar for upload and one click copy path button and hit refresh on my browser – the images were gone! I soon realized that it was because Docker created new containers because of the file changes, and the files on the original containers would be lost unless I do some backup or mount to the host.

That was when I decided to store all of the image files in MongoDB. In this way, the images are staying at the same place with the post contents, which makes backing up easy. It would also be easy to implement because I already had code for other schemas.

With some copy pasta

Schema for images:

const mongoose = require('mongoose');

const ImageShcema = new mongoose.Schema({
  data: Buffer,
  contentType: String,
}, { timestamps: true });

mongoose.model('Image', ImageShcema);

API handler for uploading and retrieving images:

const express = require('express');
const router = new express.Router();
const fs = require('fs');
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });
const auth = require('../auth');
const mongoose = require('mongoose');
const Image = mongoose.model('Image');

const { ALLOWED_EXTENSIONS, MAX_SIZE } = require('../config');

router.get('/:id', (req, res, next) => {
  const id = req.params.id;
  Image.findById(id).exec()
    .then(image => {
      if (!image) {
        return res.sendStatus(404);
      }
      res.contentType(image.contentType);
      res.send(image.data);
    })
    .catch(err => {
      if (err.name === 'CastError') {
        return res.sendStatus(404);
      }
      next(err);
    });
});

router.post('/', [auth.admin, upload.single('upload')], (req, res, next) => {
  const filename = req.file.originalname;
  const path = req.file.path;

  const splitArr = filename.split('.');
  const extension = splitArr.pop().toLowerCase();
  if (!ALLOWED_EXTENSIONS.includes(extension)) {
    removeFile(path);
    return res.status(403).json({ message: 'Forbidden file extension' });
  }

  if (req.file.size > MAX_SIZE) {
    removeFile(path);
    return res.status(403).json({ message: `File exceeds maximum allowed size: ${MAX_SIZE / 1000000} MB` });
  }

  const image = new Image({
    data: fs.readFileSync(path),
    contentType: `image/${extension}`,
  });
  image.save()
    .then(saved => res.json({ path: `uploads/${saved._id}` }))
    .then(() => removeFile(path))
    .catch(next);
});

function removeFile(path) {
  fs.unlink(path, err => {
    if (err) console.log(err);
  });
}

module.exports = router;

I had to also catch ‘CastError’ in GET because the stupid mongoose throws when the param cannot be casted into ObjectId.

Basically what I do when the user uploads is storing the file to MongoDB, deleting the file in FS, and returning the ID of the file in the database. The ID can then be used for the GET api.

I’m also proud to say that this API endpoint is unit tested and (almost) fully covered. No I wish not to discuss the overall 0.2% coverage drop for the repo.

As I said above, I also added progress bar, feedback for copying path, error prompt for invalid files and some other UI features. The GitHub issue is now closed and I now just have to wait for the requester to come back online for my demo :).

38 thoughts on “Image Uploading and Attaching for the Blog Project”

  1. Thanks for giving your ideas in this article. The other issue is that any time a problem develops with a pc motherboard, people today should not have some risk of repairing the item themselves for if it is not done properly it can lead to irreparable damage to the complete laptop. It is usually safe just to approach your dealer of your laptop for your repair of the motherboard. They’ve technicians who’ve an competence in dealing with notebook computer motherboard troubles and can get the right prognosis and conduct repairs.

  2. news USA We n we publish all of them hot and important events World, analytics experts. All bad in the world happens with the quiet tacit consent of the indifferent. No one provides us with incentives. We are Volunteers. We are building a civil society. The people are the bearer of sovereignty and the only source of power. No one can usurp power. Useful topics – Faces Seattle Business

    TWITTER
    FACEBOOK
    GOOGLE+
    Lease

  3. After reading your post, you have a great website with interesting content. But I think you can improve your current google ranks by using SEO website traffic net. My friend uses it and it works great. Just google it, it’s very nice tool to bring you a lot of new readers on a daily basis. Keep up the quality work!

  4. After reading your post, you have a great website with interesting content. But I think you can improve your current google ranks by using this service. My friend uses it and it works great. Just check to https://bit.ly/2v7j3id, it’s very nice tool to bring you a lot of new readers on a daily basis. Keep up the quality work!

  5. News portal about Russia We n we publish all of them hot and advanced events Russia, analytics experts. All evil on this planet is created with the quiet tacit consent of the indifferent. No one provides us with incentives. We are Enthusiasts. We are building a civil society. The people are the bearer of sovereignty and the only source of power. No one can usurp power. Useful topics – Health Poland Business

    TWITTER
    FACEBOOK
    GOOGLE+
    Retail

  6. News in Russia We n we publish all of them hot and important facts World, analytics experts. All bad on earth exists with the quiet tacit consent of the indifferent. No one provides us with incentives. We are Volunteers. We are building a civil society. The people are the bearer of sovereignty and the only source of power. No one can usurp power. Useful topics – USA and Russia Columbus Finance

    TWITTER
    FACEBOOK
    GOOGLE+
    zambia

  7. Economy of Russia We n we publish all of them fresh and important news USA, analytics experts. All bad in the world happens with the quiet tacit consent of the indifferent. No one provides us with incentives. We are Volunteers. We are building a civil society. The people are the bearer of sovereignty and the only source of power. No one can usurp power. Useful topics – Faces Donate News

    TWITTER
    FACEBOOK
    GOOGLE+
    arlington

  8. Hello, let’s be friends
    world news We n we publish all of them hot and important news USA, analytics experts. All negative on this planet exists with the quiet tacit consent of the indifferent. No one provides us with incentives. We are Volunteers. We are building a civil society. The people are the bearer of sovereignty and the only source of power. No one can usurp power. Useful topics – Dill Iowa Business

    Puerto-rico Finance

    TWITTER
    FACEBOOK
    GOOGLE+
    INDIA FINANCE

  9. Ukrainian political news today. We n we publish all of them hot and global events Russia, analytics experts. All evil on earth happens with the quiet tacit consent of the indifferent. No one provides us with incentives. We are Volunteers. We are building a civil society. The people are the bearer of sovereignty and the only source of power. No one can usurp power. Useful topics – Technologies and Science Vds News

    TWITTER
    FACEBOOK
    GOOGLE+
    Attorney

  10. ??USA political news. We n we publish all of them fresh and topical facts USA, estimates experts. All negative in the world is created with the quiet tacit consent of the indifferent. No one provides us with incentives. We are Enthusiasts. We are building a civil society. The people are the bearer of sovereignty and the only source of power. No one can usurp power. Useful topics – Human Rights Poland Finance

    TWITTER
    FACEBOOK
    GOOGLE+
    long-beach

  11. USA news We n we publish all of them hot and global events World, estimates experts. All evil on earth is created with the quiet tacit consent of the indifferent. No one provides us with incentives. We are Enthusiasts. We are building a civil society. The people are the bearer of sovereignty and the only source of power. No one can usurp power. Useful topics – Business Dental News

    TWITTER
    FACEBOOK
    GOOGLE+
    chicago

  12. USA news We n we publish all of them hot and important news Russia, analytics experts. All evil on earth exists with the quiet tacit consent of the indifferent. No one provides us with incentives. We are Enthusiasts. We are building a civil society. The people are the bearer of sovereignty and the only source of power. No one can usurp power. Useful topics – Georgia Sierra-leone Business legion cooking recipes vendor proficio reverse mortgage antisyn it support jacksonville

    TWITTER
    FACEBOOK
    GOOGLE+
    business

  13. I added a new blog post yesterday. There’s a smaller list
    than usual in it. I’m currently working on a bigger list.
    It’s going to take some time to compile it.

    I hope all of you are having a good week. I’m planning on doing some design changes to my site.
    I’m also thinking about adding some new things too.
    I’ll talk more about that in the days and weeks to come.

  14. Economy of Russia We n we publish all of them fresh and global news Russia, analytics experts. All negative in the world exists with the quiet tacit consent of the indifferent. No one provides us with incentives. We are Enthusiasts. We are building a civil society. The people are the bearer of sovereignty and the only source of power. No one can usurp power. Useful topics – Ukrosmi Earnings News 225 grand apartments jersey city balinese interior design ideas sdfcu flexpoints just travel deals mygecu

    TWITTER
    FACEBOOK
    GOOGLE+
    san-francisco

  15. Impressive web site. Loads of techniques in this article pilkada dki. Now i am mailing this to 3 mates ans as well sharing within delicious. And definitely, appreciate it in your efforts!

  16. I will straight away clutch i465 black your own feed while i are unable to to discover ones mail registration website link or even e-newsletter assistance. Are there just about any? You need to enable everyone understand to make sure that I might register. Thank you.. kumpulan vlogger terfavorit

  17. 名人 明星愛戴的眼鏡品牌,除了余文樂愛的Moscot,還有這些貴價貨! Marie Claire (HK) Edition 在香港,因為余交樂的加持,讓不太留意眼鏡市場的人也知多了一個眼鏡品牌,那就是Moscot了。深受Woody Allen、Johnny Depp愛戴,其中還有一款以Woody ALlen為設計靈感的復古

  18. 我們採用國際及美國食品及藥物管理局FDA認可的CO2 激光儀 LUTRONICS® SPECTRA SPR, 具安全性, 準確度高 . 二氧化碳激光可安全地去除皮膚上的癦痣、肉粒、疣、老人斑等問題。此激光的幼細光束可準確及直接地將要去除的組織氧化,過程快捷,傷口細小及乾淨,對周圍的皮膚傷害減至最少。一般1-2次就可永久去除。

  19. I simply want to mention I am just very new to blogs and certainly loved you’re web blog. More than likely I’m planning to bookmark your blog post . You absolutely have beneficial well written articles. Many thanks for sharing your web site.

  20. I spent four years trying EVERYTHING in Online Dating, and through a huge amount of trial and error, I produced a system that I will share for you. This book will take you, step by step, through everything you need to know to double, triple or even quadruple the number of women you meet online.

  21. 斐济水莲花身体润肤露斐济水莲花The Body Shop® 从 The Body Shop 购买 斐济水莲花身体润肤露 : 质感清爽的润肤露,蕴含斐济水莲花萃取物及购自公平社群贸易计划的乳木果,能迅速补充肌肤所需水分,同时释放清新轻柔的水莲花花香。配合同系列产品使用,让肌肤长时间散发清雅香气。

Leave a Reply

Your email address will not be published. Required fields are marked *