Vue 2 Express: Single / Multiple Files and Images Upload

Last Updated on by in Vue JS
In this tutorial, we will look at how to upload single, multiple files using Vue and Node and Express REST APIs.Along with that, we will also learn how to send multipart form data using FormData() web API.

File uploading is a generic process that allow to transfer a copy of a file or image to server. You can upload a single file, or you can upload multiple files at once.

To handle file upload in Vue, we will show you how to create file upload APIs from scratch.

Let’s get started.

Set Up Project

Use command to install Vue project:

vue create vue-single-multi-files-upload

Enter inside the project:

cd vue-single-multi-files-upload

To remove multi-word error warning, add the following code in vue.config.js file.

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave: false,
})

Node.js Gatsby error – “digital envelope routines::unsupported …”

Error: digital envelope routines::unsupported

opensslErrorStack: [ 'error:03000086:digital envelope routines::initialization error' ],
library: 'digital envelope routines',
reason: 'unsupported',
code: 'ERR_OSSL_EVP_UNSUPPORTED'

To remove above error for invoking the app, make sure to update the "scripts": [] array in package.json file.

"scripts": {
    "serve": "export NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve",
    "build": "export NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build",
    "lint": "export NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service lint"
},

Configure Bootstrap

Open the public/index.html file and add the Bootstrap CDN link.

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet">

Install Axios Library

Install Axios library in Vue.js app to make the API requests.

npm install axios

Build Express/Node Server

We will be uploading files and images in the remote server so that we will create a backend server using Node. Create file upload API using Express.js and store it in the MongoDB database.

Create a server folder at the root of your Vue.js project.

mkdir server && cd server

Create package.json for node server.

npm init -y

We need to install the following dependencies.

npm i --save cors express mongoose multer body-parser

We have to install the nodemon package.

npm install nodemon --save-dev

This will save our time when it comes to re-start the node server if any change is done in any of the node file.

Create Schema

Create server/models folder, create a new file and name it User.js.

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

let userSchema = new Schema({
  _id: mongoose.Schema.Types.ObjectId,
  files: {
    type: Array
  },
}, {
  collection: 'users'
})

module.exports = mongoose.model('User', userSchema)

Create File Upload API

Create a new folder server/public, and we are going to store uploaded files in this folder.

mkdir public

Next, we need to create a server/routes folder; in this folder, we have to create user.routes.js file. Add all the following code inside the routes file.

let express = require('express'),
  multer = require('multer'),
  mongoose = require('mongoose'),
  router = express.Router();

const DIR = './public/';

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, DIR);
  },
  filename: (req, file, cb) => {
    const fileName = file.originalname.toLowerCase().split(' ').join('-');
    cb(null, fileName)
  }
});

var upload = multer({
  storage: storage,
  fileFilter: (req, file, cb) => {
    if (file.mimetype == "image/png" || file.mimetype == "image/jpg" || file.mimetype == "image/jpeg") {
      cb(null, true);
    } else {
      cb(null, false);
      return cb(new Error('Only .png, .jpg and .jpeg format allowed!'));
    }
  }
});

// User model
let User = require('../models/User');

router.post('/file-upload', upload.array('files', 10), (req, res, next) => {
  const reqFiles = []
  const url = req.protocol + '://' + req.get('host')
  for (var i = 0; i < req.files.length; i++) {
    reqFiles.push(url + '/public/' + req.files[i].filename)
  }

  const user = new User({
    _id: new mongoose.Types.ObjectId(),
    files: reqFiles
  });
  user.save().then(result => {
    console.log(result);
    res.status(201).json({
      message: "Done upload!",
      userCreated: {
        _id: result._id,
        files: result.files
      }
    })
  }).catch(err => {
    console.log(err),
      res.status(500).json({
        error: err
      });
  })
})

router.get("/", (req, res, next) => {
  User.find().then(data => {
    res.status(200).json({
      message: "Data fetched!",
      users: data
    });
  });
});

module.exports = router;

The Multer package provides the upload.array method. This method takes two parameters, the file name and the total number of files to be uploaded to the server.

You can have a look at the full Multer documentation here.

Set up Node Server

Next, we need to create a server/index.js file. In this file, we define all the server related settings.

let express = require('express'),
  mongoose = require('mongoose'),
  cors = require('cors'),
  bodyParser = require('body-parser');
 

// Routes to Handle Request
const userRoute = require('../server/routes/user.routes')

// Connecting MongoDB
async function mongoDbConnection() {
    await mongoose.connect(
      "mongodb://127.0.0.1:27017/test",
      {
        useNewUrlParser: true,
        useUnifiedTopology: true,
      },
      6000
    );
  }
  mongoDbConnection().then(() => {
    console.log("MongoDB successfully connected.");
  }),
    (err) => {
      console.log("Could not connected to database : " + err);
    };


// Setup Express.js
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
  extended: false
}));
app.use(cors());

// Make Images "Uploads" Folder Publicly Available
app.use('/public', express.static('public'));

// API Route
app.use('/api', userRoute)
const port = process.env.PORT || 4000;
const server = app.listen(port, () => {
  console.log('Connected to port ' + port)
})

// Error
app.use((req, res, next) => {
  // Error goes via `next()` method
  setImmediate(() => {
    next(new Error('Something went wrong'));
  });
});
app.use(function (err, req, res, next) {
  console.error(err.message);
  if (!err.statusCode) err.statusCode = 500;
  res.status(err.statusCode).send(err.message);
});

Run the Express Server

Make sure to setup and start the MongoDB community and MongoDB Compass GUI database in your local system.

Also, pass the MongoDB database URL to the mongoose.connect(‘…’) method in the backend/index.js file.

Open the terminal window and run the following command to start the nodemon server:

npx nodemon server

Here are the file upload API we created:

We have created the File Uploading API using Node and Express.js. Now let us test out our newly built API using Postmen.vue node express file upload example

Method API
GET http://localhost:4000/api
POST http://localhost:4000/api/file-upload

Create Single & Multiple Files Uploading Component

In this step we will build Single & Multiple Files and Images Uploading component.

First, create a component/FileUpload.vue in Vue app. Then, add the following code inside of it.

<template>
  <div>
    <div class="container">
        <form @submit.prevent="handleSubmit">
            <div class="form-group">
                <input type="file" @change="uploadFile" multiple>
            </div>

            <div class="form-group">
                <button class="btn btn-success btn-block btn-lg">Upload</button>
            </div>
        </form>
    </div>    
  </div>
</template>

<script>
import axios from "axios";

export default {
  data() {
      return {
        files: null
      };
    },
    methods: {
        uploadFile (event) {
        this.files = event.target.files
        },
        handleSubmit() {
          const formData = new FormData();
          for (const i of Object.keys(this.files)) {
            formData.append('files', this.files[i])
          }
          axios.post('http://localhost:4000/api/file-upload', formData, {
          }).then((res) => {
            console.log(res)
          })
        }  
    }

}
</script>

<style scoped lang="scss">
.container {
  max-width: 600px;
}
</style>

The axios.post() method takes the file uploading API that we created using Express.js; the second parameter is the form data that we are sending to the server in multi-parts.

The Object.keys() method returns an array of a given object’s enumerable property names, here we are extracting the files from the FileList API by iterating in the same order that a standard loop does.

Head over to App.vue file, here you have to register the FileUpload component.

<template>
  <div id="app">
    <FileUpload />
  </div>
</template>

<script>
import FileUpload from './components/FileUpload.vue'

export default {
  name: 'App',
  components: {
    FileUpload
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Start the app in the browser:

npm run serve

Create Single & Multiple Files Uploading Component

Summary

So this was it, in this tutorial, we have learned how to upload files to the express server using Axios and FormData() web API.

I hope you loved this articles. Don’t forget to share it with others.