0

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?

Danchat
  • 137
  • 2
  • 12

2 Answers2

1

The issue is within your Angular application in the input field. Your ngModel is doing nothing because doesn't have the correct syntax, and even so it would only hold a fake path to the image.

There are several ways to get the actual File object from an input field. The simplest one is to use the onChange event. It would be something like this:

<input type="file" name="image" (change)="onChange($event)">

And inside your TS file:

onChange(event: any) {
    this.uploadedImage = event.target.files[0];
}

You will also need to update your upload method inside your service because multer parses a FormData, so you must send your data as a FormData.

const data = new FormData();
data.append('image', this.file, this.file.name);
return this.http.post(this.baseUrl + url, data);

Note: You are getting "File uploaded successfully" from the backend, because multer does not throw an error even if there is no data to process so your upload endpoint will always return the success code 201 even if no file was actually uploaded.

yk46
  • 130
  • 1
  • 7
  • Thanks for the response. I had some of this commented out as I've tried this before, but I will give it another shot. I'll update the main post --- I am getting the same result, no errors from multer, but no file upload. It seems to me that multer can't find the 'image' object. Do you know where that comes in from the req object? Req.body, req.file, req.files? – Danchat Oct 22 '20 at 02:04
  • It looks like it is supposed to come over via req.body and req.files. However, it needs to be encoded with type multipart/form-data. I did exactly that and it still doesn't work. - Source: https://stackoverflow.com/questions/44861517/express-body-parser-req-body-with-formdata-is-empty-object – Danchat Oct 22 '20 at 15:56
  • Are you initializing the FormData correctly? I don't see that part in your updated answer. – yk46 Oct 23 '20 at 00:17
  • It's simply ```formData = new FormData();```. I've tried it inside the uploadImage() function too but neither works. – Danchat Oct 24 '20 at 04:01
  • Thanks for your help, you pushed me in the right direction. I just needed to encode the formData properly. – Danchat Oct 26 '20 at 22:52
0

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.

Danchat
  • 137
  • 2
  • 12