Search code examples
node.jsangulartypescriptexpressmulter

Image Upload Issue using Angular, Node, Express, Multer


I've been following some guides (how to upload image file and display using express nodejs, NodeJS Multer is not working for exmaple) on how to upload an image, but I have had problems getting the image to be uploaded to by backend directory. Here is my code:

Angular Frontend

//html
<input type="file" name="image" (change)="onFileSelected($event)">
<button (click)="uploadImage()">Save Image</button>

//ts
formData = new FormData();

  onFileSelected(event) {
    this.uploadedImage = event.target.files[0]
}

uploadImage() {
    if (this.uploadedImage) {
      this.formData.append('image', this.uploadedImage, this.uploadedImage.name)
      console.log(this.formData.getAll('image')) //confirms file is being uploaded properly
      this.httpService.upload('uploadImage/', this.formData).subscribe((message: any) => {
        console.log(message)
      });
}

//httpService
  upload(url, file) {
    console.log("uploading file")
    return this.http.post(this.baseUrl + url, file);
  }

Node Backend

//index.js
var http = require('http');
var express = require('express');
var cors = require('cors');
var app = express();
var multer = require('multer')
var path = require('path')
var bodyParser = require('body-parser');

app.use(cors());

const storage = multer.diskStorage({
    destination: (req, file, cb) => {
        cb(null, path.resolve(__dirname, '../documents/'));
    },
    filename: (req, file, cb) => {
        console.log(file);
        cb(null, Date.now() + path.extname(file.originalname));
    }
});
const fileFilter = (req, file, cb) => {
    if (file.mimetype == 'image/jpeg' || file.mimetype == 'image/png') {
        cb(null, true);
    } else {
        cb(null, false);
    }
}
const upload = multer({ storage: storage, fileFilter: fileFilter });

app.use(bodyParser.json());

app.use(function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "http://localhost:4200");
    res.header('Access-Control-Allow-Methods', 'GET,POST,PATCH,PUT,DELETE,OPTIONS');
    res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
    next();
})

// other API requests...

app.post('/api/uploadImage/', upload.single('image'), (req, res, next) => {
    console.log(req.file) //returns undefined
    try {
        return res.status(201).json({
            message: 'File uploaded successfully'
        });
    } catch(error) {
        console.error(error);
    }
});

var server = http.createServer(app)

server.listen(5000, function() {
  console.log('Http Server is up and running.');
}

I get the message 'File uploaded successfully' from the backend after the HttpService is finished, but there is no image in my /documents/ directory. From what I can tell, the image should be coming over through the req.file or req.files variable, but returns undefined. What am I doing wrong here?


Solution

  • I discovered that the issue was related to the FormData's inability to transmit to the backend, and needed to be encoded correctly. Following an explanation here, I changed up the HTML to look like this:

    <form enctype="multipart/form-data" (ngSubmit)="uploadImage()">
       <input type="file" name="image" (change)="onFileSelected($event)">
       <button>Save Image</button>
    </form>
    

    The backend received the request through req.body and the file was successfully uploaded.