VueJS 3, ExpressJS 4.17 | Uploading and resizing an image

Uploading an image and resizing with VueJS 3 and ExpressJS 4.17


I will show you how to make a simple form with VueJS 3, to upload an image and a text field that could be used for the image’s alternative text.

The server is running ExpressJS 4.17.

We will need several libraries :

VueJS Front

  • Axios (V0.21) to make HTTP requests to the server


Back ExpressJS

  • Multer (V 1.4.2) to retrieve the form image and insert it in the folder
  • Sharp (V 0.26.3) to resize the image and put it in a thumbnail folder
  • Cors (V 2.8.5) in the case of a Cross origin problem


Once the outbuildings are installed, let’s get started.

VueJS form


All we need is an input file and a text input.

<template>
  <div id='main'>
      <input type="file" name="picture" id="picture">
      <!-- Input text linked to a view variablejs for the alternative text of the image -->
      <input type="text" name="alt-picture" id="" placeholder="alt text" v-model="alt_text">
      <!-- The send button linked to a send function -->
      <button @click="send()">Send</button>
  </div>
</template>

We give an id to the input file to retrieve its picture later, and we link the text input to a variable for a real-time refresh.

We also need our function to send the form to the server using Axios!

Here is the script of the VueJS component:

<script>
import axios from 'axios'

export default {
  name: 'App',
   data() {
    return {
      alt_text: ''
    }
  },
  methods: {
    send(){
      // Image recovery
      let img = document.getElementById('picture').files[0]
      // Creation of an obligatory formData for sending the image
        var formData = new FormData()
        formData.append('img', img)
        formData.append('alt_text', this.alt_text)
        // Sending data on the server url (put your own) in POST by sending the formData containing our image and our text
        axios.post('http://localhost:3000/upload_image', formData)
          .then((resp) => {
            console.log(resp)
          })
          .catch((err) => {
            console.log(err.response)
          })
    }
  }
}
</script>

So we need to import Axios, create our method that will be called by the send button, and in it get our image, put it in a formData with our alternate text and send it all to the ExpressJS server via Axios.

ExpressJS server side


First of all for our server it is necessary to import our dependencies and to set up the Cross origin :

//Importance of our dependencies
const express = require('express')
const multer = require('multer')
const sharp = require('sharp')
const path = require('path')
var cors = require('cors')

const app = express()

app.use(express.json())

// Implementation of Cross origin to avoid errors in vuejs
app.use(cors())
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*')
  // authorized headers for preflight requests
  // https://developer.mozilla.org/en-US/docs/Glossary/preflight_request
  res.header(
    'Access-Control-Allow-Headers',
    'Origin, X-Requested-With, Content-Type, Accept',
  )
  next()

  app.options('*', (req, res) => {
    // allowed XHR methods
    res.header(
      'Access-Control-Allow-Methods',
      'GET, PATCH, PUT, POST, DELETE, OPTIONS',
    )
    res.send()
  })
})

Then we will attack the configuration part of Multer so that the library knows where we want to upload the image and other parameters.

After the cross origin set up the Multer configuration:

// Creation of multer's diskStorage, it allows to define our upload configuration
// /!\ Create the destination folders just in case before uploading
var storage = multer.diskStorage({
  // The file size limit
  limits: {
    fileSize: 1000000, //1Mo
  },
  // The destination, here it will be at the root in the img folder.
  destination: function (req, file, cb) {
    cb(null, './img')
  },
  // Error handling
  fileFilter(req, file, cb) {
    if (!file.originalname.match(/\.(jpg|jpeg|png)$/)) {
      return cb(new Error('Le fichier doit etre un JPG'))
    }
    cb(undefined, true)
  },
  // Function that renames the image
  filename: function (req, file, cb) {
    // Generate a random name and retrieve the old extension
    cb(
      null,
      Math.random().toString(36).substring(7) +
        '.' +
        file.originalname.split('.')[1],
    )
  },
})

// Creation of the multer object
const upload = multer({
  storage: storage,
})

You can read in the comments what each thing is used for, globally Multer allows us to choose a destination folder, to apply rules of limits and file extension, as well as to be able to rename the image.

Route POST of image reception


It is now necessary to set up our route which will receive our superb images, as well as to launch our server!

// Using the multer object as middleware
// This means that first multer will upload our image, and then we go to the function of our /upload_image route.
app.post('/upload_image', upload.single('img'), async (req, res) => {
  try {
    if (req.file) {
      // Uses the sharp library to resize to 200x100, and returns the thumbnail to another file in the destination folder chosen in the diskStorage.
      await sharp(req.file.path, { failOnError: false })
        .resize({ width: 200, height: 100 })
        .toFile(
          path.resolve(req.file.destination + '/thumbnail', req.file.filename),
        )
      // You can use these variables to make insertions in databases or other applications
      let filename = req.file.filename
      let alt_text = req.body.alt_text
    }
    res.send('Upload fini')
  } catch (e) {
    res.status(400).send(e)
  }
})

app.listen(3000, () => {
  console.log('Server launch on port : ' + 3000)
})

We created our route, set up the middleware that allows us to upload the image, and then we defined the function of the route as asynchronous for the resizing of our image.

After uploading the main image, Sharp will take it and resize it, in the example I put 200×100 but it is possible to set any value.

After resizing, Sharp puts our file in the destination folder of our main image in a thumbnail folder, thanks to the .toFile() method.

Here too you can choose any destination.

Then we can have access to the alternative text as well as the name of the image if we want to store this data in a database to display it under VueJS later!

Images in the folder

VueJS, ExpressJS, image upload and folder resizing

Before and after images


Here is a preview of the before and after images:

VueJS, ExpressJS, image upload and folder before

You can see that the sum of the two images is 454kb, the images are 1280*853px and 640x401px.

And after the resizing :

VueJS, ExpressJS, image upload and folder after

We save about 97% of storage space thanks to resizing.

This tutorial is now finished, thanks for reading and feel free to share 🙂