162

I need to upload an image, and display it, as well as save it so that I don't lose it when I refresh the localhost. This needs to be done using an "Upload" button, which prompts for a file-selection.

I am using node.js and express for the server-side code.

Cœur
  • 37,241
  • 25
  • 195
  • 267
user1602123
  • 1,677
  • 2
  • 11
  • 5
  • 1
    you can also use multer for file or image uploading and sharp js for image processing and doing resizing or compressing and other things on images – Emad Baqeri May 10 '21 at 09:13

1 Answers1

341

First of all, you should make an HTML form containing a file input element. You also need to set the form's enctype attribute to multipart/form-data:

<form method="post" enctype="multipart/form-data" action="/upload">
    <input type="file" name="file">
    <input type="submit" value="Submit">
</form>

Assuming the form is defined in index.html stored in a directory named public relative to where your script is located, you can serve it this way:

const http = require("http");
const path = require("path");
const fs = require("fs");

const express = require("express");

const app = express();
const httpServer = http.createServer(app);

const PORT = process.env.PORT || 3000;

httpServer.listen(PORT, () => {
  console.log(`Server is listening on port ${PORT}`);
});

// put the HTML file containing your form in a directory named "public" (relative to where this script is located)
app.get("/", express.static(path.join(__dirname, "./public")));

Once that's done, users will be able to upload files to your server via that form. But to reassemble the uploaded file in your application, you'll need to parse the request body (as multipart form data).

In Express 3.x you could use express.bodyParser middleware to handle multipart forms but as of Express 4.x, there's no body parser bundled with the framework. Luckily, you can choose from one of the many available multipart/form-data parsers out there. Here, I'll be using multer:

You need to define a route to handle form posts:

const multer = require("multer");

const handleError = (err, res) => {
  res
    .status(500)
    .contentType("text/plain")
    .end("Oops! Something went wrong!");
};

const upload = multer({
  dest: "/path/to/temporary/directory/to/store/uploaded/files"
  // you might also want to set some limits: https://github.com/expressjs/multer#limits
});


app.post(
  "/upload",
  upload.single("file" /* name attribute of <file> element in your form */),
  (req, res) => {
    const tempPath = req.file.path;
    const targetPath = path.join(__dirname, "./uploads/image.png");

    if (path.extname(req.file.originalname).toLowerCase() === ".png") {
      fs.rename(tempPath, targetPath, err => {
        if (err) return handleError(err, res);

        res
          .status(200)
          .contentType("text/plain")
          .end("File uploaded!");
      });
    } else {
      fs.unlink(tempPath, err => {
        if (err) return handleError(err, res);

        res
          .status(403)
          .contentType("text/plain")
          .end("Only .png files are allowed!");
      });
    }
  }
);

In the example above, .png files posted to /upload will be saved to uploaded directory relative to where the script is located.

In order to show the uploaded image, assuming you already have an HTML page containing an img element:

<img src="/image.png" />

you can define another route in your express app and use res.sendFile to serve the stored image:

app.get("/image.png", (req, res) => {
  res.sendFile(path.join(__dirname, "./uploads/image.png"));
});
Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
fardjad
  • 20,031
  • 6
  • 53
  • 68
  • 11
    For anybody looking to access 'req.files' or 'req.body', body-parser now only handles JSON, check out https://github.com/expressjs/multer – Scott Meyers Feb 25 '15 at 23:58
  • 5
    as "app.use(express.bodyParser({uploadDir:'...'}));" is not longer working one should use "app.use(bodyParser({uploadDir:'...'}));". therefor body-parser has to be added via npm and added to the file you using it in via "var bodyParser = require('body-parser');" – Niklas Zantner Oct 31 '15 at 16:00
  • 5
    how we can do this in express 4? – Muhammad Shahzad Apr 12 '16 at 07:49
  • 4
    @fardjad What if I have angular in between ? – Gaurav51289 Apr 12 '16 at 20:59
  • 1
    @fardjad the req.files.file.path gives me "cannot read property 'file' of undefined" error. Doing everything you did except the express.bodyparser() since I am using express 4.x I am doing app.use(bodyParser.urlencoded({'extended':'true'})); – codeinprogress Jun 09 '16 at 16:45
  • 4
    For express 4 use multer middle ware for file upload.. – Yuvaraj V Apr 04 '17 at 08:52
  • To show we can use a public file path. `/static/avatars/18746365356.png` – uruapanmexicansong Aug 05 '17 at 19:33
  • for those who get `ENOENT: no such file or directory, rename` error, you can install fs-extra and use fs.ensureDir to ensure the target path is exist. – principiorum Jan 18 '20 at 03:31